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(); }

In reply to Should this be a Windows Service ? by hennesse

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.