hennesse has asked for the wisdom of the Perl Monks concerning the following question:
Should this be a Windows Service ?
I have a large, multi-threaded daemon program which I’m porting from FreeBSD to Windows, and I’m not sure what the correct approach is.
The daemon interfaces with some industrial process control hardware, and the user interfaces with the daemon through a web browser. In Unix, the user just types Start or Stop on the command line to control the daemon.
For the Windows implementation, I want to have a System Tray icon which, when clicked, brings up a little GUI window that has “Start” and “Stop” buttons, and a little more. Whenever the user turns on his computer, the System Tray icon should be there, and the daemon should not be running.
Having never done such a thing before (I’m an Unix guy), my first thought was to bundle both the System Tray icon (Win32::GUI) and the main daemon starter/stopper into a Service (code below). This works beautifully from the point of view of starting and stopping the daemon from the little window, but I suspect it’s a really, really bad Windows service.
My next thought was that maybe I don’t really need a Service after all – if I can get the icon to somehow (?) always appear in the System Tray, the icon program can start the daemon by adding new threads, and stop it by telling those threads to exit.
Or maybe the System Tray Icon should be one program, and the daemon be a service.
Whoosh! I’m confused – what’s the right approach to this problem?
Thanks – Dave
#!/usr/bin/perl # ---------------------------------------------------------------- # starter.pl # ---------------------------------------------------------------- # # Windows environment only # # Registers as a Windows service # Displays an icon in the System Tray # The icon opens a GUI window with Start/Stop buttons # Start opens supervisor and the 4 dwarves in new threads # Stop sends a "goodbye" message to supervisor, # who then relays the goodbye to the dwarves # # ---------- ---- ---------------------------------------------- # DATE VERS DESCRIPTION # ---------- ---- ---------------------------------------------- # 2011-10-21 1.00 First Version # ---------- ---- ---------------------------------------------- # ---------------------------------------------------------------- # Directives # ---------------------------------------------------------------- use threads; use threads::shared; use thread::Queue; use strict; use Socket; use Fcntl; use Carp; use Errno; use Time::HiRes qw ( time alarm sleep ); use Brewery; use PID; use SSR; use Win32::Daemon; use Win32::GUI(); # ---------------------------------------------------------------- # Variables # ---------------------------------------------------------------- my $thr1; my $thr2; my $thr3; my $thr4; my $thr5; my %Context; # ---------------------------------------------------------------- # Main Program # ---------------------------------------------------------------- Win32::Daemon::RegisterCallbacks( { start => \&Callback_Start, running => \&Callback_Running, stop => \&Callback_Stop, pause => \&Callback_Pause, continue => \&Callback_Continue, } ); %Context = ( last_state => SERVICE_STOPPED, start_time => time(), ); # Start the service passing in a context and # indicating to callback using the "Running" event # every 2000 milliseconds (2 seconds). Win32::Daemon::StartService( \%Context, 2000 ); sub Callback_Running { my( $Event, $Context ) = @_; # Note that here you want to check that the state # is indeed SERVICE_RUNNING. Even though the Running # callback is called it could have done so before # calling the "Start" callback. if( SERVICE_RUNNING == Win32::Daemon::State() ) { chdir("c:/Documents and Settings/Owner/Desktop/WWW/brewery/threads +"); require "c:/Documents and Settings/Owner/Desktop/WWW/brewery/threa +ds/supervisor.pl"; require "c:/Documents and Settings/Owner/Desktop/WWW/brewery/threa +ds/ctrlman.pl"; require "c:/Documents and Settings/Owner/Desktop/WWW/brewery/threa +ds/pidman.pl"; require "c:/Documents and Settings/Owner/Desktop/WWW/brewery/threa +ds/progman.pl"; require "c:/Documents and Settings/Owner/Desktop/WWW/brewery/threa +ds/webman.pl"; my $splashimage= new Win32::GUI::Bitmap("c:/Documents and Settings +/Owner/Desktop/WWW/brewery/threads/teb.bmp"); my $main = Win32::GUI::Window->new( -name => 'Main', -width => 220, -height => 100, -text => 'The Electronic Brewery', -background => '#c0c0c0' ); my $theImage = $main->AddLabel( -bitmap => $splashimage, -pos => [0,0] ); my $theLabel = $main->AddLabel( -text => 'The Brewery is: STOPPED', -pos => [40,25] ); my $start_button = $main->AddButton( -name => 'Start', -text => 'Start', -pos => [ 60, 50 ] ); my $stop_button = $main->AddButton( -name => 'Stop', -text => 'Stop', -visible => 0, -pos => [ 100, 50 ] ); my $icon = new Win32::GUI::Icon('c:/Documents and Settings/Owner/D +esktop/WWW/brewery/favicon.ico'); my $ni = $main->AddNotifyIcon( -name => "NI", -icon => $icon, -tip => "The Electronic Brewery" ); Win32::GUI::Dialog(); sub Main_Terminate { $main->Disable(); $main->Hide(); return 0; } sub Main_Minimize { $main->Disable(); $main->Hide(); return 1; } sub NI_Click { $main->Enable(); $main->Show(); return 1; } sub Start_Click { $theLabel->Text('The Brewery is RUNNING'); $start_button->Hide(); $stop_button->Show(); $thr1 = threads->create(\&supervisor); $thr2 = threads->create(\&ctrlman); $thr3 = threads->create(\&pidman); $thr4 = threads->create(\&progman); $thr5 = threads->create(\&webman); return 0; } sub Stop_Click { $theLabel->Text('The Brewery is STOPPED'); $start_button->Show(); $stop_button->Hide(); my $iaddr = inet_aton("127.0.0.1"); my $proto = getprotobyname("udp"); my $port = 0; my $paddr = sockaddr_in( $port, $iaddr ); socket( SUPERVISOR, PF_INET, SOCK_DGRAM, $proto ) || die "sock +et: $!"; bind( SUPERVISOR, $paddr ) || die "bind: $!"; $port = 5000; my $dest = sockaddr_in($port, $iaddr); send(SUPERVISOR, "goodbye", 0, $dest); return 0; } } } sub Callback_Start { my( $Event, $Context ) = @_; # Initialization code # ...do whatever you need to do to start... $Context->{last_state} = SERVICE_RUNNING; Win32::Daemon::State( SERVICE_RUNNING ); } sub Callback_Pause { my( $Event, $Context ) = @_; $Context->{last_state} = SERVICE_PAUSED; Win32::Daemon::State( SERVICE_PAUSED ); } sub Callback_Continue { my( $Event, $Context ) = @_; $Context->{last_state} = SERVICE_RUNNING; Win32::Daemon::State( SERVICE_RUNNING ); } sub Callback_Stop { my( $Event, $Context ) = @_; $Context->{last_state} = SERVICE_STOPPED; Win32::Daemon::State( SERVICE_STOPPED ); # We need to notify the Daemon that we want to stop callbacks +and the service. Win32::Daemon::StopService(); }
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Should this be a Windows Service ?
by BrowserUk (Patriarch) on Oct 21, 2011 at 19:35 UTC | |
|
Re: Should this be a Windows Service ?
by hennesse (Beadle) on Oct 21, 2011 at 21:10 UTC | |
|
Re: Should this be a Windows Service ?
by locked_user sundialsvc4 (Abbot) on Oct 22, 2011 at 13:33 UTC |