#//////////////////////////////////////////////////////////////////////////////
#//
#//  Daemon.pm
#//  Win32::Daemon Perl extension package file
#//
#//  Copyright (c) 1998-2002 Dave Roth
#//  Courtesy of Roth Consulting
#//  http://www.roth.net/
#//
#//  This file may be copied or modified only under the terms of either
#//  the Artistic License or the GNU General Public License, which may
#//  be found in the Perl 5.0 source kit.
#//
#//  2008.03.24  :Date
#//  20080324    :Version
#//////////////////////////////////////////////////////////////////////////////

package Win32::Daemon;


#use strict;
#use warnings;
our $PACKAGE = $Package = "Win32::Daemon";

our $VERSION = 20080324;
require Exporter;
require DynaLoader;
use vars qw( $AUTOLOAD );

my @OSVerInfo = Win32::GetOSVersion();
my $OSVersion = "$OSVerInfo[1].$OSVerInfo[2]";
my $RECOGNIZED_CONTROLS;

our @ISA= qw( Exporter DynaLoader );
    # Items to export into callers namespace by default. Note: do not export
    # names by default without a very good reason. Use EXPORT_OK instead.
    # Do not simply export all your public functions/methods/constants.
our @EXPORT = qw(

    SERVICE_CONTROL_USER_DEFINED
    SERVICE_NOT_READY
    SERVICE_STOPPED
    SERVICE_RUNNING
    SERVICE_PAUSED
    SERVICE_START_PENDING
    SERVICE_STOP_PENDING
    SERVICE_CONTINUE_PENDING
    SERVICE_PAUSE_PENDING

    SERVICE_CONTROL_NONE
    SERVICE_CONTROL_STOP
    SERVICE_CONTROL_PAUSE
    SERVICE_CONTROL_CONTINUE
    SERVICE_CONTROL_INTERROGATE
    SERVICE_CONTROL_SHUTDOWN
    SERVICE_CONTROL_PARAMCHANGE
    SERVICE_CONTROL_NETBINDADD
    SERVICE_CONTROL_NETBINDREMOVE
    SERVICE_CONTROL_NETBINDENABLE
    SERVICE_CONTROL_NETBINDDISABLE
    SERVICE_CONTROL_DEVICEEVENT
    SERVICE_CONTROL_HARDWAREPROFILECHANGE
    SERVICE_CONTROL_POWEREVENT
    SERVICE_CONTROL_SESSIONCHANGE
    SERVICE_CONTROL_USER_DEFINED
    SERVICE_CONTROL_RUNNING
	SERVICE_CONTROL_PRESHUTDOWN

    SERVICE_ACCEPT_DEVICEEVENT
    SERVICE_ACCEPT_HARDWAREPROFILECHANGE
    SERVICE_ACCEPT_POWEREVENT
    SERVICE_ACCEPT_SESSIONCHANGE

    USER_SERVICE_BITS_1
    USER_SERVICE_BITS_2
    USER_SERVICE_BITS_3
    USER_SERVICE_BITS_4
    USER_SERVICE_BITS_5
    USER_SERVICE_BITS_6
    USER_SERVICE_BITS_7
    USER_SERVICE_BITS_8
    USER_SERVICE_BITS_9
    USER_SERVICE_BITS_10

    SERVICE_ACCEPT_STOP
    SERVICE_ACCEPT_PAUSE_CONTINUE
    SERVICE_ACCEPT_SHUTDOWN
    SERVICE_ACCEPT_PARAMCHANGE
    SERVICE_ACCEPT_NETBINDCHANGE

    SERVICE_WIN32_OWN_PROCESS
    SERVICE_WIN32_SHARE_PROCESS
    SERVICE_KERNEL_DRIVER
    SERVICE_FILE_SYSTEM_DRIVER
    SERVICE_INTERACTIVE_PROCESS

    SERVICE_BOOT_START
    SERVICE_SYSTEM_START
    SERVICE_AUTO_START
    SERVICE_DEMAND_START
    SERVICE_DISABLED

    SERVICE_DISABLED
    SERVICE_ERROR_NORMAL
    SERVICE_ERROR_SEVERE
    SERVICE_ERROR_CRITICAL

    SC_GROUP_IDENTIFIER

    NO_ERROR
);


our @EXPORT_OK = ();

bootstrap(__PACKAGE__);

sub AUTOLOAD
{
    # This AUTOLOAD is used to 'autoload' constants from the constant()
    # XS function.  If a constant is not found then control is passed
    # to the AUTOLOAD in AutoLoader.

    my( $Constant ) = $AUTOLOAD;
    my( $Result, $Value );
    $Constant =~ s/.*:://;

    $Result = Constant( $Constant, $Value );

    if( 0 == $Result )
    {
        # The extension could not resolve the constant...
        $AutoLoader::AUTOLOAD = $AUTOLOAD;
            goto &AutoLoader::AUTOLOAD;
        return;
    }
    elsif( 1 == $Result )
    {
        # $Result == 1 if the constant is valid but not defined
        # that is, the extension knows that the constant exists but for
        # some wild reason it was not compiled with it.
        my ($pack,$file,$line) = caller;
        print "Your vendor has not defined ".__PACKAGE__." macro $Constant, used in $file at line $line.";
    }
    elsif( 2 == $Result )
    {
        # If $Result == 2 then we have a string value
        $Value = "'$Value'";
    }
        # If $Result == 3 then we have a numeric value

    eval "sub $AUTOLOAD { return( $Value ); }";
    goto &$AUTOLOAD;
}


# For a module, you *always* return TRUE...
return( 1 );


END
{
    # Stop the service only if we are in the main thread (not in a forked process)
    Win32::Daemon::StopService() if $$ > 0;
}

__END__

=head1 NAME

Win32::Daemon - Extension enabling Win32 Perl scripts to run as a true Win32 service.

=head1 SYNOPSIS

    use Win32::Daemon;
    Win32::Daemon::StartService();
    # ...process Perl code...
    Win32::Daemon::StopService();

=head1 DESCRIPTION

This extension enables a Win32 Perl script to act as a true Win32 service.

=head1 FUNCTIONS

=head2 Function List

=over 4

C<AcceptedControls()>

C<CallbackTimer()>

C<CreateService()>

C<ConfigureService()>

C<QueryServiceConfig()>

C<DeleteService()>

C<GetLastError()>

C<GetSecurity()>

C<GetServiceHandle()>

C<HideService()>

C<QueryLastMessage()>

C<RegisterCallbacks()>

C<RestoreService()>

C<SetSecurity()>

C<SetServiceBits()>

C<ShowService()>

C<StartService()>

C<State()>

C<StopService()>

C<Timeout()>

=back

=head2 Function Descriptions

=over 4

=item AcceptedControls( [$NewControls] )

This function queries (and optionally sets) the current list of controls that the service registers for.
By registering for a control the script is notifying the SCM that it is accepting the specified
control messages. For example, if you specify the C<SERVICE_ACCEPT_PAUSE_CONTINUE> control then
the SCM knows that the script will accept and process any attempt to pause and continue (resume
from paused state) the service.

Recognized accepted controls:

    SERVICE_ACCEPT_STOP...............The service accepts messages to stop.
    SERVICE_ACCEPT_PAUSE_CONTINUE.....The service accepts messages to pause and continue.
    SERVICE_ACCEPT_SHUTDOWN...........The service accepts messages to shutdown the system.
                                      When the OS is shutting down the service will be notified
                                      when it has accepted this control.

Following controls are only recognized on Windows 2000 and higher:

    SERVICE_ACCEPT_PARAMCHANGE........The service accepts messages notifying it of any
                                      parameter change made to the service.
    SERVICE_ACCEPT_NETBINDCHANGE......The service accepts messages notifying it of any
                                      network binding changes.

By default all of these controls are accepted. To change this pass in a value consisting of
any of these values OR'ed together.

B<NOTE> that you can query and set these controls at any time. However it is only supported to
set them before you start the service (calling the C<StartService()> function).

=item CallbackTimer( [ $NewTimerValue ] )

This function returns the value of the callback timer. The value is in milliseconds.
This value indicates how often the "Running" callback subroutine will be called. Note
that the calling of this routine will be blocked by any other callback.

If you pass in a value it will reset the timer to the specified frequency. Passing in
a 0 will disable all "Running" callbacks. Passing in -1 will toggle the state between
calling the "Running" callback subroutine and not calling it.

=item CreateService ( \%Hash )

This function creates a new service. The return is TRUE if the service was
created, and FALSE otherwise. If an error occurred, call GetLastError
to retrieve the actual error code.

The hash describes the service to be created. The keys are:

=over 4

=item name

The 'internal' service name; that is, the name of the
registry key used to store the information on this service.

=item display

The 'display' service name; that is, the name displayed
by the services control panel or MMC plugin.

=item path

The full path name to the executable. This should be the path to your Perl
executable, which will normally be the contents of $^X.

B<NOTE:> If you are using a compiled perl script (such as one
generated with PerlApp) as opposed to a text based perl script file then this
value must point to the actual compiled script's executable (eg. MyCompiledPerlService.exe)
instead of ($^X which usually points to perl.exe). You can specify
any parameters to pass into the service using the "parameters" key.

=item user

The username the service is to run under; this is optional.

=item password

The password to be used to log in the service; this is
technically optional, but needs to be specified if {user} is.

=item parameters

The parameters to be passed to Perl; in other words, the command line you
would execute interactively, but without the leading ``perl ''. The 'parameters' key
value is appended to the "path" key when starting the service.
Typically this will be something like:

    <code>MyPerlScript.pl /a /b /c</code>


=item machine

The name of the machine to create the service on. Omission
or an empty string specify the machine executing the call.

=item service_type

An integer representing the type of the service;
defaults to C<SERVICE_WIN32_OWN_PROCESS>.

=item start_type

An integer specifying how (or whether) the service is
to be started. The default is C<SERVICE_AUTO_START>.

=item error_control

An integer specifying how the Service Control Manager
is to react if the service fails to start. The default is
C<SERVICE_ERROR_IGNORE>, which in fact gets you an error log entry.

=item load_order

The name of the load order group of which this service
is a member. The default is membership in no group. See value
ServiceGroupOrder in registry key
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
for the names.

=item tag_id

An integer representing the startup order of the service
within its load ordering group.

=item dependencies

A reference to the 'internal' names of services and/or
load ordering groups upon which this service depends. The default is
no dependencies. Load order group names are prefixed with a '+' to
distinguish them from service names.

=item description

A short text description of the service, displayed
(at least) as flyover help by the MMC "services" plugin.

=back

=item DeleteService ($Machine, $ServiceName )

This function deletes an existing service. The return is TRUE if the
service was deleted, and FALSE otherwise. If an error occurred, call
GetLastError to retrieve the actual error code.

The arguments are the name of the machine (an empty string specifies
the machine executing the call), and the 'internal' service name (i.e.
the string passed in the {name} element when the service was created).

A running service may not be deleted.



=item GetSecurity( $Machine, $ServiceName )

This will return a binary Security Descriptor (SD) that is associated with the
specified service on the specified machine.

The SD is in self-relative format. It can be imported into a Win32::Perms object using
the Win32::Perms object's Import() method.

=item RegisterCallbacks( $CodeRef | \%Hash )

This will register specified code subroutines that will be called when specified
events take place. For example if you register a subroutine called Pause() with the
pause event then this routine will be called when there is an attempt to pause the
service. Not all events must have callbacks registered.

If only a reference to a subroutine is passed in then it will be called for each and every
event. You can pass in a hash containing particular key names (listed below) with
code references.

Possible hash key names:

    Key Name                 Event
    -------------            --------------------------------------
    start....................The service is starting.
    pause....................The service is entering a paused state.
    continue.................The service is resuming from a paused state.
    stop.....................The service is stopping (see note below).
    running..................The service is running (see note below).
    interrogate..............The service is being queried for information.
    shutdown.................The system is being shut down.
    preshutdown..............The system is about to begin shutting down (Vista+ only).
    param_change.............There has been a parameter change to the system.
    net_bind_add.............A new network binding has been made.
    net_bind_remove..........A network binding has been removed.
    net_bind_enable..........A network binding has been enabled.
    net_bind_disable.........A network binding has been disabled.
    device_event.............A device has generated some event.
    hardware_profile_change..A change has been made to the system's hardware profile.
    power_event..............A power event has occured (eg change to battery power).
    session_change...........There has been a change in session.
    user_defined.............A user defined event has been sent to the service.

NOTE: The 'Stop' state. When a service calls into the registered "stop" callback routine
the script should call the C<StopService()> function. This tells the service to terminate
and return back to the Perl script. This is the only way for the service to know that it
must stop.

Note: The 'Running' state. Periodically the extension will call into a registered
"Running" subroutine. This allows the script to process data. This routine should be fast
and return quickly otherwise it will block other callback events from being run. The
frequency of calling the "Running" subroutine is dictated by the callback timer value
passed into C<StartService()> and any changes made to this value by calling into
C<CallbackTimer()>.

=item SetSecurity( $Machine, $ServiceName, $BinarySD | $Win32PermsObject )

This applies the specified Security Descriptor (SD) to the specified service on the
specified machine. You must have appropriate permissions to call this function.

The specified SD can be either a binary SD (in self-relative or absolute format) or
it can be a Win32::Perms object.

This only sets the DACL and SACL. The owner and group are not set even if they are
specified in the SD.



=item StartService( [ \%Context, $CallbackTimer ] )

This starts a new service thread. The script should call this as soon as possible.  When
the service manager starts the service Perl is started and the script is loaded.

This function returns the thread handle of the service thread. If you call into this more
than once it will only return the thread handle (it won't create another new service thread).

=over 4

=item Callback Mode

If the script has already registered callback routines (using C<RegisterCallbacks()>) then
the call into C<StartService()> will not return until the service has stopped. However
callbacks will be made for each state change and callback timer timeout (refer to C<RegisterCallbacks()>).

=back

=item StopService()

This will instruct the service to terminate.

=item Timeout( [$TimeoutValue] )

This function sets the new timeout value indicating how long a command will wait before
Win32::Daemon tells the Service Control Manager that the command failed.

=item QueryLastMessage( [$fResetMessage] )

This function returns the last message that the service manager has sent to the service.

Pass in a non zero value to reset the pending message to C<SERVICE_CONTROL_NONE>. This way
your script can tell when two of the same messages come in.

Occasionally the service manager will send messages to the service. These messages
typically request the service to change from one state to another.  It is important that
the Perl script responds to each message otherwise the service manager becomes confused
about the current state of the service. For example, if the service manager is submits
a C<SERVICE_PAUSE_PENDING> then it expects the Perl script to recognize the change to a paused
state and submit the new state by calling C<State( SERVICE_PAUSED )>.

You can update the service manager with the current status using the C<State()> function.

Possible values returned are:

    Valid Service Control Messages:
	-------------------------------
    SERVICE_CONTROL_NONE..............No message is pending.
    SERVICE_CONTROL_STOP..............The SCM is requesting the service to stop.
                                      This results in State() reporting SERVICE_STOP_PENDING.
    SERVICE_CONTROL_PAUSE.............The SCM is requesting the service to pause.
                                      This results in State() reporting SERVICE_PAUSE_PENDING.
    SERVICE_CONTROL_CONTINUE..........The SCM is requesting the service to continue from a
                                      paused state.
                                      This results in State() reporting SERVICE_CONTINUE_PENDING.
    SERVICE_CONTROL_INTERROGATE.......The service manager is querying the service's state

    SERVICE_CONTROL_USER_DEFINED......This is a user defined control. There are 127 of these
                                      beginning with SERVICE_CONTROL_USER_DEFINED as the base.
    Windows 2000 specific messages:
    SERVICE_CONTROL_SHUTDOWN..........The machine is shutting down. This indicates that
                                      the service has roughly 20 seconds to clean up
                                      and terminate. This time can be extended by
                                      submitting SERVICE_STOP_PENDING via the State() function.

    SERVICE_CONTROL_PARAMCHANGE.......Service parameters have been modified.
    SERVICE_CONTROL_NETBINDADD........A network binding as been added.
    SERVICE_CONTROL_NETBINDREMOVE.....A network binding has been removed.
    SERVICE_CONTROL_NETBINDENABLE.....A network binding has been enabled.
    SERVICE_CONTROL_NETBINDDISABLE....A network binding has been disabled.
    SERVICE_CONTROL_DEVICEEVENT.......A device has generated some event.
    SERVICE_CONTROL_HARDWAREPROFILECHANGE..A change has been made to the system's hardware profile.
    SERVICE_CONTROL_POWEREVENT........A power event has occured (eg change to battery power).
    SERVICE_CONTROL_SESSIONCHANGE.....There has been a change in session.

    Windows Vista + specific messages:
    SERVICE_CONTROL_PRESHUTDOWN ......The machine is about to shut down. This provides the service
                                      much more time to shutdown than SERVICE_CONTROL_SHUTDOWN.



B<Note:> When the system shuts down it will send a C<SERVICE_CONTROL_SHUTDOWN> message. The
Perl script has approximately 20 seconds to perform any shutdown activities before the
Control Manger stops the service. If more time is needed call the C<State()> function
passing in the C<SERVICE_STOP_PENDING> control message along with how many seconds it will
take to shutdown the service. This time value is only an estimate. When the service is
finally ready to stop it must submit the C<SERVICE_STOPPED> message as in:

    if( SERVICE_CONTROL_SHUTDOWN == State() )
    {
        Win32::Daemon::State( SERVICE_STOP_PENDING, 30 );
        #...process code...
        Win32::Daemon::State( SERVICE_STOPPED );
    }


=item State([$NewState [, $Hint ] || \%Hash ] )

This function returns the current state of the service.  It can optionally update the
status of the service as well.  This is the last status reported to the service manager.

Optionally you can pass in a value that will be sent to the service manager.
Optionally you can pass in a numeric value indicating the "hint". This is the number of
milliseconds the SCM can expect to wait before the service responds to the request. For example,
if your service script reports a hint of 30,000 milliseconds means that the SCM will have to wait
for 30 seconds for the script to change the service's state before deciding that the
script is non responsive.

If you are setting/updating the state instead of passing in the state and wait hint you could
pass in a hash reference. This allows you to specify the state, wait hint and error state. You
can use the following keys:

    Hash Key
    --------
    state..........Valid service state (see table below).
    waithint.......A wait hint explained above. This is in milliseconds.
    error..........Any 32 bit error code. This is what will be reported if an application
                   queries the error state of the service. It is also what is reported if
                   a call to start the services fails.
                   To reset an error state pass in NO_ERROR.
                   The only invalid error value is 0xFFFFFFFF.

Example of passing in an error:

  Win32::Daemon::State( { error => 0x12345678 } );
  # Later to reset the error:
  Win32::Daemon::State( { error => NO_ERROR } );


Possible values returned (or submitted):

	Valid Service States:
	---------------------
    SERVICE_NOT_READY..........The SCM has not yet been initialized. If the SCM is slow or busy
                               then this value will result from a call to State().
                               If you get this value, just keep calling State() until you get
                               SERVICE_START_PENDING.
    SERVICE_STOPPED............The service is stopped
    SERVICE_RUNNING............The service is running
    SERVICE_PAUSED.............The service is paused
    SERVICE_START_PENDING......The service manager is attempting to start the service
    SERVICE_STOP_PENDING.......The service manager is attempting to stop the service
    SERVICE_CONTINUE_PENDING...The service manager is attempting to resume the service
    SERVICE_PAUSE_PENDING......The service manager is attempting to pause the service

=back

=head1 Callbacks

Callbacks were introduced in version v20030617.

The Win32::Daemon supports the concept of event callbacks. This allows a script to
register a particular subroutine with a particular event. When the event occurs it
will call the Perl subroutine registered with that event. This can make it very simple
to write scripts.

You register a callback subroutine by calling into the C<RegisterCallbacks()> function.
You can pass in a code reference or a hash. A code reference will register the specified
subroutine with all events. A hash allows you to pick which events you want to
register for which subroutines. You do not have to register all events. If an event is
not registered for a subroutine then the script will not be notified when the event
occurs.

At a minimum a script should register for the 'Start' and 'Running' states. This enables
the script to actually start and to periodically process data.

When an event callback occurs the subroutine should change the state accordingly by
passing in the new state into C<State()>. For example the 'Start' callback would call
C<State( SERVICE_RUNNING )> to inform the service that it is officially running. Another
example is the 'Pause' state should call C<State( SERVICE_PAUSED )> to inform the service
that it is offically paused.

Once callback subroutines are registered the script enters the service mode by calling
C<StartService()>. This will being the process of calling the event callback routines.
Note that when callback routines are registered the C<StartService()> function will not
return until a callback routine calls C<StopService()> (typically the 'Stop' event callback
would call C<StopService()>.

When calling into C<StartService()> you can pass in a hash reference. This reference is known as
a "context" hash. For every callback the hash will be passed into the callback routine. This enables
a script to query and set data in the hash--essentially letting you pass information across to
different callback events. This context hash is not required.

When a callback is made it always passes two parameters in: $State and $Context. $State is simply
the state change that caused the callback. This represents the event that took place (e.g. C<SERVICE_PAUSE_PENDING>,
C<SERVICE_START_PENDING>, etc). The $Context is a reference to the context hash that was passed into
the C<StartService()> function.

A typical callback routine should look similar to:

    sub Callback_Start
    {
        my( $Event, $Context ) = @_;
        $Context->{last_event} = $Event;

        # ...do some work here...

        # Tell the service manager that we have now
        # entered the running state.
        Win32::Daemon::State( SERVICE_RUNNING );
        return();
    }


Refer to C<Example 4: Using a single callback> and C<Example 5: Using different callback routines> for an example of using callbacks.

=head1 Compiled Perl Applications

Many users like to compile their perl scripts into executable programs. This way it is much easier to copy them around
from machine to machine since all necessary files, packages and binaries are compiled into one .exe file. These compiled
perl scripts are compatible with Win32::Deamon as long as you install it correctly.

If you are going to compile your Win32::Daemon based perl script into an .exe there is nothing unique you need to do
to your Win32::Daemon code with one single exception of the call into Win32::Daemon::C<CreateService()>. When passing in
the 'path' and 'parameters' values into C<CreateService()> observe the following simple rules:

=over 4

    1) If using a Perl script
      path........The full path to the Perl interpeter (perl.exe).
                  This is typically:
                     c:\perl\bin\perl.exe

      parameter...This value MUST start with the full path to the
                  perl script file and append any parameters
                  that you want passed into the service. For
                  example:
                  c:\scripts\myPerlService.pl -param1 -param2 "c:\\Param2Path"

    2) If using a compiled Perl application
      path........The full path to the compiled Perl application.
                  For example:
                  c:\compiledscripts\myPerlService.exe

      parameter...This value is just the list of  parameters
                  that you want passed into the service. For
                  example:
                     -param1 -param2 "c:\\Param2Path"

=back

Refer to C<Example 3: Install the service> for an example.


=head1 Examples

=head2 Example 1: Simple Service

This example service will delete all .tmp files from the c:\temp directory every
time it starts.  It will immediately terminate.

    use Win32::Daemon;

    # Tell the OS to start processing the service...
    Win32::Daemon::StartService();

    # Wait until the service manager is ready for us to continue...
    while( SERVICE_START_PENDING != Win32::Daemon::State() )
    {
        sleep( 1 );
    }

    # Now let the service manager know that we are running...
    Win32::Daemon::State( SERVICE_RUNNING );

    # Okay, go ahead and process stuff...
    unlink( glob( "c:\\temp\\*.tmp" ) );

    # Tell the OS that the service is terminating...
    Win32::Daemon::StopService();

This particular example does not really illustrate the capabilities of a Perl based service.

=head2 Example 2: Typical skeleton code
  # This style of Win32::Daemon use is obsolete. It still works but the
  # callback model is more efficient and easier to use. Refer to examples 4 and 5.
  use Win32;
  use Win32::Daemon;
  $SERVICE_SLEEP_TIME = 20; # 20 milliseconds
  $PrevState = SERVICE_START_PENDING;
  Win32::Daemon::StartService();
  while( SERVICE_STOPPED != ( $State = Win32::Daemon::State() ) )
  {
    if( SERVICE_START_PENDING == $State )
    {
        # Initialization code
        Win32::Daemon::State( SERVICE_RUNNING );
        $PrevState = SERVICE_RUNNING;
    }
    elseif( SERVICE_STOP_PENDING == $State )
    {
      Win32::Daemon::State( SERVICE_STOPPED );
    }
    elsif( SERVICE_PAUSE_PENDING == $State )
    {
      # "Pausing...";
      Win32::Daemon::State( SERVICE_PAUSED );
      $PrevState = SERVICE_PAUSED;
      next;
    }
    elsif( SERVICE_CONTINUE_PENDING == $State )
    {
      # "Resuming...";
      Win32::Daemon::State( SERVICE_RUNNING );
      $PrevState = SERVICE_RUNNING;
      next;
    }
    elsif( SERVICE_STOP_PENDING == $State )
    {
      # "Stopping...";
      Win32::Daemon::State( SERVICE_STOPPED );
      $PrevState = SERVICE_STOPPED;
      next;
    }
    elsif( SERVICE_RUNNING == $State )
    {
      # The service is running as normal...
      # ...add the main code here...

    }
    else
    {
      # Got an unhandled control message. Set the state to
      # whatever the previous state was.
      Win32::Daemon::State( $PrevState );
    }

	# Check for any outstanding commands. Pass in a non zero value
	# and it resets the Last Message to SERVICE_CONTROL_NONE.
	if( SERVICE_CONTROL_NONE != ( my $Message = Win32::Daemon::QueryLastMessage( 1 ) ) )
	{
          if( SERVICE_CONTROL_INTERROGATE == $Message )
          {
            # Got here if the Service Control Manager is requesting
            # the current state of the service. This can happen for
            # a variety of reasons. Report the last state we set.
            Win32::Daemon::State( $PrevState );
          }
        elsif( SERVICE_CONTROL_SHUTDOWN == $Message )
        {
          # Yikes! The system is shutting down. We had better clean up
          # and stop.
          # Tell the SCM that we are preparing to shutdown and that we expect
          # it to take 25 seconds (so don't terminate us for at least 25 seconds)...
          Win32::Daemon::State( SERVICE_STOP_PENDING, 25000 );
        }
      }
      # Snooze for awhile so we don't suck up cpu time...
      Win32::Sleep( $SERVICE_SLEEP_TIME );
   }
   # We are done so close down...
   Win32::Daemon::StopService();


=head2 Example 3: Install the service

For the 'path' key the $^X equates to the full path of the
perl executable.
Since no user is specified it defaults to the LocalSystem.

    use Win32::Daemon;
    # If using a compiled perl script (eg. myPerlService.exe) then
    # $ServicePath must be the path to the .exe as in:
    #    $ServicePath = 'c:\CompiledPerlScripts\myPerlService.exe';
    # Otherwise it must point to the Perl interpreter (perl.exe) which
    # is conviently provided by the $^X variable...
    my $ServicePath = $^X;

    # If using a compiled perl script then $ServiceParams
    # must be the parameters to pass into your Perl service as in:
    #    $ServiceParams = '-param1 -param2 "c:\\Param2Path"';
    # OTHERWISE
    # it MUST point to the perl script file that is the service such as:
    my $ServiceParams = 'c:\perl\scripts\myPerlService.pl -param1 -param2 "c:\\Param2Path"';


    %Hash = (
        machine =>  '',
        name    =>  'PerlTest',
        display =>  'Oh my GOD, Perl is a service!',
        path    =>  $ServicePath,
        user    =>  '',
        pwd     =>  '',
        description => 'Some text description of this service',
        parameters => $ServiceParams
    );
    if( Win32::Daemon::CreateService( \%Hash ) )
    {
        print "Successfully added.\n";
    }
    else
    {
        print "Failed to add service: " . Win32::FormatMessage( Win32::Daemon::GetLastError() ) . "\n";
    }

NOTES:
    - ConfigureService:
        - If you specify a 'parameters' key you MUST specify a 'path' key.


=head2 Example 4: Using a single callback

In this example only one subroutine is used for all callbacks. The CallbackRoutine()
subroutine will receive all event callbacks. Basically this callback routine will
have to do essentially the same thing that the main while loop in C<Example 2> does.

    use Win32::Daemon;
    Win32::Daemon::RegisterCallbacks( \&CallbackRoutine );
    %Context = (
        count   =>  0,
        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 CallbackRoutine
    {
        my( $Event, $Context ) = @_;

        $Context->{last_event} = $Event;
        if( SERVICE_RUNNING == $Event )
        {
            # ... process your main stuff here...
            # ... note that here there is no need to
            #     change the state

        }
        elsif( SERVICE_START_PENDING == $Event )
        {
            # Initialization code
            # ...do whatever you need to do to start...

            $Context->{last_state} = SERVICE_RUNNING;
            Win32::Daemon::State( SERVICE_RUNNING );
        }
        elsif( SERVICE_PAUSE_PENDING == $Event )
        {
            $Context->{last_state} = SERVICE_PAUSED;
            Win32::Daemon::State( SERVICE_PAUSED );

        }
        elsif( SERVICE_CONTINUE_PENDING == $Event )
        {
            $Context->{last_state} = SERVICE_RUNNING;
            Win32::Daemon::State( SERVICE_RUNNING );
        }
        elsif( SERVICE_STOP_PENDING == $Event )
        {
            $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();
        }
        else
        {
            # Take care of unhandled states by setting the State()
            # to whatever the last state was we set...
            Win32::Daemon::State( $Context->{last_state} );
        }
        return();
    }


=head2 Example 5: Using different callback routines

    use Win32::Daemon;
    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() )
        {
            # ... process your main stuff here...
            # ... note that here there is no need to
            #     change the state
        }
    }

    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();
    }




=head1 HISTORY:

    - 20011205 rothd
        -Fixed bug where "invalid service state 80" is reported in the Win32 event log.
        -Added initial support for SCM request callbacks (this is not implemented fully and
         is not supported yet).
        -Added SERVICE_CONTROL_NONE constant
        -Fixed State() so it only returns states, not commands.
        -Modified QueryLastMessage(). It now allows you to reset the last message to SERVICE_CONTROL_NONE.
        -Added SERVICE_NOT_READY state
        -Cleaned up some states.
        -Added AcceptedControls() function.

    - 20011217 rothd
        - Added support for .Net Server accept control contants:
            SERVICE_ACCEPT_HARDWAREPROFILECHANGE
            SERVICE_ACCEPT_POWEREVENT
            SERVICE_ACCEPT_SESSIONCHANGE

    - 20011221 rothd@roth.net
        - Fixed bug where service doesn't work properly with Windows NT 4. We were
          defaulting by acccepting the SERVICE_ACCEPT_PARAMCHANGE and
          SERVICE_ACCEPT_NETBINDCHANGE controls. However, they were introduced in
          Win2k so NT 4 coughed up blood with them.

    - 20020108 rothd@roth.net
        - Fixed another bug wwhere servie wouldn't work on Win2k machines. We
          were treating Win2k (version 5.0) the same as WinXP (version 5.1) and
          adding WinXP specific controls. This rendered the service unable to
          start, pause or stop. Fix was only in the Daemon.pm file.

    - 20020114 marc.pijnappels@nec-computers.com
        - Fixed another bug where service wouldn't work on WinXP machines. Variable
          recognized_controls was wrongly initialized for WinXP systems. This
          rendered the service unable to start, pause or stop. Fix was only in
          the Daemon.pm file.

    - 20020605 rothd@roth.net
        - Added support for reporting service errors. You can now pass in a
          hash reference into State(). More details in the POD docs.

    - 20030617 rothd@roth.net
        - Added support for querying and setting service security. This includes
          DACL and SACL support. Due to complexity and failures you can not set
          owner or group. But in most cases (99%) this is not needed.
          - GetSecurity()
          - SetSecurity()
        - Finished incorporating callback routines.
        - Added a few more events.

	- 20080321	rothd@roth.net
	   -Callbacks can now return a state value. This is the functional equivilent of calling
	    Win32::Daemon::State( $NewState ) before returning:
			//	sub EventCallback
			//	{
			//	  my( $Event, $Context ) = @_;
			//		# Process the event
			//		Win32::Daemon::State( $NewState );		
			//	  return;
			//	}
			//	=================== SAME AS ====================
			//	sub EventCallback
			//	{
			//	  my( $Event, $Context ) = @_;
			//		# Process the event
			//	  return( $NewState );
			//	}
		
	  -Callback timer works correctly!
	  -CALLBACK_RUNNING has been depreciated and replaced with CALLBACK_TIMER. It is only called
	   when the timer (set by CallbackTimer()) has expired. Use this as a heartbeat. You only need
	   to set it once either with CallbackTimer() or passing in the timer value when calling into
	   StartService().
	   Set the callback using "timer" as the callback name. Using "running" will also work but it
	   is mapped to "timer". If you specify both, only "timer" will be registered.
	