Back to the WFC main page

CServiceControlManager

$Revision: 41 $

Description

This class handles playing with NT's Service Control Manager (SCM). The SCM manages the starting/stopping/installing/removing/etc. of services.

Data Members

None.

Methods

void Close( void )
Closes your session with the SCM.
BOOL Continue( LPCTSTR service_name )
You give this function the name of a paused service. It will tell the named service to continue.
BOOL EnableInteractiveServices( BOOL enable_interactive_services = TRUE )
This function returns TRUE if the setting was set, FALSE if there was a problem. Calling EnableInteractiveServices() with enable_interactive_services set to FALSE will prevent any service from interacting with the desktop/user.
BOOL EnumerateStatus( DWORD state = SERVICE_ACTIVE, type = SERVICE_WIN32 )
This function allows you to enumerate services that have a particular status. The state parameter may be one of:
  • SERVICE_ACTIVE
  • SERVICE_INACTIVE
  • SERVICE_STATE_ALL
The type parameter may be one of:
  • SERVICE_WIN32
  • SERVICE_DRIVER
BOOL GetConfiguration( LPCTSTR service_name, CServiceConfiguration& configuration )
This function retrieves useful information about how a service is configured.
BOOL GetDependencies( LPCTSTR service_name, CStringArray& dependencies )
Fills dependencies with the names of services that must be running before the service_name service can run.
DWORD GetErrorCode( void ) const
Retrieves the error code. Call this function to find out why any other class member returned FALSE.
SC_HANDLE GetHandle( void ) const
Returns the encapsulated SC_HANDLE. Use this method if you want to call the Win32 API directly (i.e. you don't want to use WFC).
BOOL GetKeyName( LPCTSTR display_name, CString& key_name )
Retrieves a service's internal name (real name) from the user-friendly name.
BOOL GetNext( CServiceNameAndStatus& status )
This function is called to retrieve the next name and status (CServiceNameAndStatus) being enumerated via EnumerateStatus. It will return FALSE when the last enumerated service has been retrieved.
BOOL Install( LPCTSTR service_name, LPCTSTR friendly_name, LPCTSTR name_of_executable_file )
This function will install a new service.
BOOL IsDatabaseLocked( CString& who_locked_it, CTimeSpan& how_long_it_has_been_locked )
If IsDatabaseLocked() returns TRUE, the database is locked, who_locked_it will be filled with the name of the user who locked it, how_long_it_has_been_locked will be filled with how long that user has had the database locked. If IsDatabaseLocked() returns FALSE, who_locked_it will be empty and how_long_it_has_been_locked will be zero.
BOOL LockDatabase( void )
Locks the service control database. Only one process can lock the database at any given time. You cannot start a service when the database is locked.The lock prevents the service control manager from starting a service while it is being reconfigured.
BOOL Open( DWORD   what_to_open  = SC_MANAGER_ALL_ACCESS,
           LPCTSTR database_name = NULL,
           LPCTSTR machine_name  = NULL )
Opens a connection to the SCM on a machine.
BOOL Pause( LPCTSTR service_name )
Pauses the named service.
BOOL Remove( LPCTSTR service_name )
Un-installs a service. WARNING! This does a lot of stuff for you. One of the things it will screw up is the Event Log. It removes the source of messages so you will no longer be able to read entries in the log that the removed service made.
SetConfiguration( LPCTSTR service_name,
                  DWORD   when_to_start           = SERVICE_NO_CHANGE,
                  DWORD   type_of_service         = SERVICE_NO_CHANGE,
                  DWORD   error_control           = SERVICE_NO_CHANGE,
                  LPCTSTR name_of_executable_file = NULL,
                  LPCTSTR load_order_group        = NULL,
                  LPCTSTR dependencies            = NULL,
                  LPCTSTR start_name              = NULL,
                  LPCTSTR password                = NULL,
                  LPCTSTR display_name            = NULL )
Changes a service's configuration. You can change when a service is to start, type, name of the executable file, etc.
BOOL Start( LPCTSTR service_name, DWORD service_argc = 0, LPCTSTR *service_argv = NULL )
Starts a service by name. You supply the startup parameters.
BOOL Stop( LPCTSTR service_name )
Stops a service by name.
BOOL UnlockDatabase( void )
Unlocks the service control database.

Example

#include <wfc.h>
#include "messages.h"
#pragma hdrstop

#if defined( _DEBUG )
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

DWORD WINAPI worker_thread( LPVOID pointer_to_parent_CService_class );
VOID set_default_parameters( void );

int __cdecl _tmain( int number_of_command_line_arguments, LPCTSTR command_line_arguments[] )
{
   WFCTRACEINIT( TEXT( "_tmain()" ) );

   if ( number_of_command_line_arguments == 1 )
   {
      CService service( worker_thread );
      service.Initialize( TEXT( "WatchDog" ) );
      return( EXIT_SUCCESS );
   }

   if ( number_of_command_line_arguments == 2 )
   {
      if ( _tcsicmp( command_line_arguments[ 1 ], TEXT( "install" ) ) == 0 )
      {
         CServiceControlManager service_control_manager;

         service_control_manager.Open();

         if ( service_control_manager.Install( TEXT( "WatchDog" ),
                                               TEXT( "WatchDog" ),
             TEXT( "%SystemRoot%\\System32\\WatchDog.exe" ) ) == FALSE )
         {
            _tprintf( TEXT( "Install failed, please see Application Log for details\n" ) );
         }

         set_default_parameters();

         return( EXIT_SUCCESS );
      }
      else if ( _tcsicmp( command_line_arguments[ 1 ], TEXT( "remove" ) ) == 0 )
      {
         CServiceControlManager service_control_manager;

         service_control_manager.Open();

         if ( service_control_manager.Remove( TEXT( "WatchDog" ) ) == FALSE )
         {
            _tprintf( TEXT( "Removing failed, please see Application Log for details\n" ) );
         }

         return( EXIT_SUCCESS );
      }
      else if ( _tcsicmp( command_line_arguments[ 1 ], TEXT( "run" ) ) == 0 )
      {
         worker_thread( (LPVOID) 1 );
         return( EXIT_SUCCESS );
      }
      else
      {
         _tprintf( TEXT( "Samuel R. Blackburn, WFC Sample Application\nUsage: %s [install|remove]\n" ), command_line_arguments[ 0 ] );
      }
   }
   else
   {
      _tprintf( TEXT( "Samuel R. Blackburn, WFC Sample Application\nUsage: %s [install|remove]\n" ), command_line_arguments[ 0 ] );
   }

   return( EXIT_SUCCESS );
}

DWORD WINAPI worker_thread( LPVOID )
{
   WFCTRACEINIT( TEXT( "worker_thread()" ) );

   {
      CEventLog log( TEXT( "WatchDog" ) );
      log.Report( CEventLog::eventInformation, 0, MSG_WATCHDOG_SERVICE_STARTED );
   }

   CStringArray names_of_services_to_keep_alive;

   DWORD number_of_seconds_to_sleep = 0;

   CString machine_name( TEXT( "" ) );

   BOOL return_value = TRUE;

   {
      CRegistry registry;

      if ( registry.Connect( CRegistry::keyLocalMachine ) == FALSE )
      {
         return( 0 );
      }

      CString key_name( TEXT( "SYSTEM\\CurrentControlSet\\Services\\WatchDog\\Parameters" ) );

      if ( registry.Open( key_name ) == FALSE )
      {
         return( 0 );
      }

      registry.GetValue( TEXT( "Services" ), names_of_services_to_keep_alive );
      registry.GetValue( TEXT( "NumberOfSecondsBetweenChecks" ), number_of_seconds_to_sleep );
      registry.GetValue( TEXT( "MachineName" ), machine_name );
   }

   DWORD sleep_time = 1000 * number_of_seconds_to_sleep;

   if ( sleep_time < 2000 )
   {
      // Minimum sleep time is 2 seconds, this give the OS time to do other things
      sleep_time = 2000;
   }

   int number_of_services_to_keep_alive = names_of_services_to_keep_alive.GetSize();

   CServiceControlManager service_control_manager;

   /*
   ** Sleep for one minute, this is in case we are starting during boot-up. We want
   ** to give the service control manager time to start all necessary services before
   ** we begin restarting stopped services.
   */

   Sleep( 60 );

   do
   {
      if ( machine_name.IsEmpty() )
      {
         return_value = service_control_manager.Open( GENERIC_READ, NULL, (LPCTSTR) NULL );
      }
      else
      {
         return_value = service_control_manager.Open( GENERIC_READ, NULL, machine_name );
      }

      if ( return_value != FALSE )
      {
         if ( service_control_manager.EnumerateStatus( SERVICE_INACTIVE ) != FALSE )
         {
            CStringArray stopped_services;

            CServiceNameAndStatus status;

            while( service_control_manager.GetNext( status ) != FALSE )
            {
               stopped_services.Add( status.lpServiceName );
            }

            // Now that we have the service names, we need to see which services need to be started

            int number_of_stopped_services = stopped_services.GetSize();
            int alive_index                = 0;
            int stopped_index              = 0;

            while( alive_index < number_of_services_to_keep_alive )
            {
               stopped_index = 0;

               while( stopped_index < number_of_stopped_services )
               {
                  if ( names_of_services_to_keep_alive[ alive_index ].CompareNoCase( stopped_services[ stopped_index ] ) == 0 )
                  {
                     // We found one that died, let's start it

                     service_control_manager.Start( names_of_services_to_keep_alive[ alive_index ] );

                     // We restarted a service, time to record the event

                     LPCTSTR string_array[ 1 ];

                     string_array[ 0 ] = (LPCTSTR) names_of_services_to_keep_alive[ alive_index ];

                     CEventLog event_log( TEXT( "WatchDog" ) );

                     event_log.Report( CEventLog::eventInformation,
                                       0,
                                       MSG_WATCHDOG_RESTARTING_SERVICE,
                                       1,
                           (LPCTSTR *) string_array );

                     // pop out of the loop

                     stopped_index = number_of_stopped_services;
                  }

                  stopped_index++;
               }

               alive_index++;
            }
         }
      }

      service_control_manager.Close();

      Sleep( sleep_time );
   }
   while( 1 );

   return( 0 );
}

void set_default_parameters( void )
{
   WFCTRACEINIT( TEXT( "set_default_parameters()" ) );

   CRegistry registry;

   if ( registry.Connect( CRegistry::keyLocalMachine ) != FALSE )
   {
      if ( registry.Create( TEXT( "SYSTEM\\CurrentControlSet\\Services\\WatchDog\\Parameters" ) != FALSE ) )
      {
         DWORD default_sleep_time = 60;

         if ( registry.SetValue( TEXT( "NumberOfSecondsBetweenChecks" ), default_sleep_time ) == FALSE )
         {
            LPVOID message_buffer = (LPVOID) NULL;

            ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                             NULL,
                             registry.GetErrorCode(),
                             MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
                   (LPTSTR) &message_buffer,
                             0,
                             NULL );

            LPCTSTR string_array[ 2 ];

            string_array[ 0 ] = TEXT( "SYSTEM\\CurrentControlSet\\Services\\WatchDog\\Parameters\\NumberOfSecondsBetweenChecks" );
            string_array[ 1 ] = (LPCTSTR) message_buffer;

            CEventLog event_log( TEXT( "WatchDog" ) );

            event_log.Report( CEventLog::eventError,
                              0,
                              MSG_CANT_SET_REGISTRY_ENTRY,
                              2,
                  (LPCTSTR *) string_array );

            if ( message_buffer != NULL )
            {
               ::LocalFree( message_buffer );
            }
         }

         CStringArray strings;

         strings.RemoveAll();
         strings.Add( TEXT( "" ) );

         if ( registry.SetValue( TEXT( "Services" ), strings ) == FALSE )
         {
            LPVOID message_buffer = (LPVOID) NULL;

            ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                             NULL,
                             registry.GetErrorCode(),
                             MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
                   (LPTSTR) &message_buffer,
                             0,
                             NULL );

            LPCTSTR string_array[ 2 ];

            string_array[ 0 ] = TEXT( "SYSTEM\\CurrentControlSet\\Services\\WatchDog\\Parameters\\Services" );
            string_array[ 1 ] = (LPCTSTR) message_buffer;

            CEventLog event_log( TEXT( "WatchDog" ) );

            event_log.Report( CEventLog::eventError, 0, MSG_CANT_SET_REGISTRY_ENTRY, 2, (LPCTSTR *) string_array );

            if ( message_buffer != NULL )
            {
               ::LocalFree( message_buffer );
            }
         }
      }
      else
      {
         LPVOID message_buffer = (LPVOID) NULL;

         ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                          NULL,
                          registry.GetErrorCode(),
                          MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
                (LPTSTR) &message_buffer,
                          0,
                          NULL );

         LPCTSTR string_array[ 2 ];

         string_array[ 0 ] = TEXT( "SYSTEM\\CurrentControlSet\\Services\\WatchDog\\Parameters" );
         string_array[ 1 ] = (LPCTSTR) message_buffer;

         CEventLog event_log( TEXT( "WatchDog" ) );

         event_log.Report( CEventLog::eventError, 0, MSG_CANT_CREATE_REGISTRY_KEY, 2, (LPCTSTR *) string_array );

         if ( message_buffer != NULL )
         {
            ::LocalFree( message_buffer );
         }
      }
   }
   else
   {
      LPVOID message_buffer = (LPVOID) NULL;

      ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                       NULL,
                       registry.GetErrorCode(),
                       MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
             (LPTSTR) &message_buffer,
                       0,
                       NULL );

      LPCTSTR string_array[ 2 ];

      string_array[ 0 ] = TEXT( "keyLocalMachine" );
      string_array[ 1 ] = (LPCTSTR) message_buffer;

      CEventLog event_log( TEXT( "WatchDog" ) );

      event_log.Report( CEventLog::eventError, 0, MSG_CANT_CONNECT_TO_REGISTRY, 2, (LPCTSTR *) string_array );

      if ( message_buffer != NULL )
      {
         ::LocalFree( message_buffer );
      }
   }
}

API's Used

CServiceControlManager encapsulates the following API's:
Copyright, 2000, Samuel R. Blackburn
$Workfile: csvcmgr.cpp $
$Modtime: 1/17/00 9:22a $