Microsoft KB Archive/245230

From BetaArchive Wiki

HOWTO: Programmatically Stop a Service and Its Dependencies

Q245230



The information in this article applies to:


  • Microsoft Win32 Application Programming Interface (API), included with:
    • Microsoft Windows NT Server version 4.0
    • Microsoft Windows NT Workstation version 4.0
    • the operating system: Microsoft Windows 2000





SUMMARY

The Service Control Manager (SCM) will not attempt to stop a service if other running services are dependent on it. In order to programmatically stop such a service, you must first enumerate and stop its dependencies. The sample code in the "More Information" section of this article demonstrates how to use the EnumDependentServices() API to stop the service.



MORE INFORMATION

When a service is registered with the SCM in the service database, its dependencies are also established. The SCM guarantees that dependencies will automatically be started prior to the starting of the service itself.

A service can be stopped through the ControlService() API by sending a SERVICE_CONTROL_STOP request to the service through the SCM. If the SCM receives a SERVICE_CONTROL_STOP request for a service, it instructs the service to stop by forwarding the stop code on to the service's ServiceMain() function. However, if the SCM determines that other running services are dependent on the specified service, it will not forward the stop request. Instead, it returns error code 1051 (ERROR_DEPENDENT_SERVICES_RUNNING) -- "A stop control has been sent to a service that other running services are dependent on."


Sample Code

The following code implements a StopService() function which will optionally attempt to stop the specified service's dependencies.

//**********************************************************************
// 
//  This program demonstrates how to programmatically stop a service 
//  by first stopping its dependencies.
// 
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
//  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
// 
//  Copyright (C) 1999 Microsoft Corporation. All rights reserved.
// 
//**********************************************************************

#include <windows.h>
#include <tchar.h>
#include <stdio.h>


//**********************************************************************
// 
//  StopService()
// 
//  PURPOSE :     This function attempts to stop a service. It allows
//                the caller to specify whether dependent services
//                should also be stopped. It also allows a timeout
//                value to be passed, to prevent a scenario in which a
//                service shutdown hangs, and in turn the application
//                stopping the service hangs.
// 
//  PARAMETERS:   hSCM - open handle to the service control manager
//                hService - open handle to the service to be stopped
//                fStopDependencies - flag indicating whether to stop
//                   dependent services
//                dwTimeout - maximum time (in milliseconds) to wait
//                   for the service and its dependencies to stop
// 
//  RETURN VALUE: If the operation is successful, ERROR_SUCCESS is 
//                returned. Otherwise, a Win32 error code is returned.
// 
//**********************************************************************

DWORD StopService( SC_HANDLE hSCM, SC_HANDLE hService, 
      BOOL fStopDependencies, DWORD dwTimeout ) {

   SERVICE_STATUS ss;
   DWORD dwStartTime = GetTickCount();

   // Make sure the service is not already stopped
   if ( !QueryServiceStatus( hService, &ss ) )
      return GetLastError();

   if ( ss.dwCurrentState == SERVICE_STOPPED ) 
      return ERROR_SUCCESS;

   // If a stop is pending, just wait for it
   while ( ss.dwCurrentState == SERVICE_STOP_PENDING ) {

      Sleep( ss.dwWaitHint );
      if ( !QueryServiceStatus( hService, &ss ) )
         return GetLastError();

      if ( ss.dwCurrentState == SERVICE_STOPPED )
         return ERROR_SUCCESS;

      if ( GetTickCount() - dwStartTime > dwTimeout )
         return ERROR_TIMEOUT;
   }

   // If the service is running, dependencies must be stopped first
   if ( fStopDependencies ) {

      DWORD i;
      DWORD dwBytesNeeded;
      DWORD dwCount;

      LPENUM_SERVICE_STATUS   lpDependencies = NULL;
      ENUM_SERVICE_STATUS     ess;
      SC_HANDLE               hDepService;

      // Pass a zero-length buffer to get the required buffer size
      if ( EnumDependentServices( hService, SERVICE_ACTIVE, 
         lpDependencies, 0, &dwBytesNeeded, &dwCount ) ) {

         // If the Enum call succeeds, then there are no dependent
         // services so do nothing

      } else {
         
         if ( GetLastError() != ERROR_MORE_DATA )
            return GetLastError(); // Unexpected error

         // Allocate a buffer for the dependencies
         lpDependencies = (LPENUM_SERVICE_STATUS) HeapAlloc( 
               GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded );

         if ( !lpDependencies )
            return GetLastError();

         __try {

            // Enumerate the dependencies
            if ( !EnumDependentServices( hService, SERVICE_ACTIVE, 
                  lpDependencies, dwBytesNeeded, &dwBytesNeeded,
                  &dwCount ) )
               return GetLastError();

            for ( i = 0; i < dwCount; i++ ) {

               ess = *(lpDependencies + i);

               // Open the service
               hDepService = OpenService( hSCM, ess.lpServiceName, 
                     SERVICE_STOP | SERVICE_QUERY_STATUS );
               if ( !hDepService )
                  return GetLastError();

               __try {

                  // Send a stop code
                  if ( !ControlService( hDepService, SERVICE_CONTROL_STOP,
                        &ss ) )
                     return GetLastError();

                  // Wait for the service to stop
                  while ( ss.dwCurrentState != SERVICE_STOPPED ) {

                     Sleep( ss.dwWaitHint );
                     if ( !QueryServiceStatus( hDepService, &ss ) )
                        return GetLastError();

                     if ( ss.dwCurrentState == SERVICE_STOPPED )
                        break;

                     if ( GetTickCount() - dwStartTime > dwTimeout )
                        return ERROR_TIMEOUT;
                  }

               } __finally {

                  // Always release the service handle
                  CloseServiceHandle( hDepService );

               }

            }

         } __finally {

            // Always free the enumeration buffer
            HeapFree( GetProcessHeap(), 0, lpDependencies );

         }
      } 
   }

   // Send a stop code to the main service
   if ( !ControlService( hService, SERVICE_CONTROL_STOP, &ss ) )
      return GetLastError();

   // Wait for the service to stop
   while ( ss.dwCurrentState != SERVICE_STOPPED ) {

      Sleep( ss.dwWaitHint );
      if ( !QueryServiceStatus( hService, &ss ) )
         return GetLastError();

      if ( ss.dwCurrentState == SERVICE_STOPPED )
         break;

      if ( GetTickCount() - dwStartTime > dwTimeout )
         return ERROR_TIMEOUT;
   }

   // Return success
   return ERROR_SUCCESS;
}


//**********************************************************************
// 
//  DisplayError()
// 
//  PURPOSE :     This is a helper function to display an error message 
//                if a function in _tmain() fails.
// 
//  PARAMETERS:   szAPI - the name of the function that failed
// 
//                dwError - the Win32 error code indicating why the
//                function failed
// 
//  RETURN VALUE: None
// 
//**********************************************************************

void DisplayError( LPTSTR szAPI, DWORD dwError ) {

   LPTSTR lpBuffer = NULL;

   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
         FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
         (LPTSTR) &lpBuffer, 0, NULL );

   _tprintf( TEXT("%s failed:\n"), szAPI );
   _tprintf( TEXT("    error code = %u\n"), dwError );
   _tprintf( TEXT("    message    = %s\n"), lpBuffer );

   LocalFree( lpBuffer );
}


//**********************************************************************
// 
//  _tmain() -- becomes main() for ANSI or wmain() for Unicode
// 
//  PURPOSE :     This is the entry point for the program. This function
//                contains sample code demonstrating how to use the
//                StopService() function implemented above.
// 
//  PARAMETERS:   argc - the number of command-line arguments
//                argv[] - an array of command-line arguments
// 
//  RETURN VALUE: None
// 
//**********************************************************************

void _tmain( int argc, TCHAR *argv[] ) {

   SC_HANDLE hSCM;
   SC_HANDLE hService;
   DWORD     dwError;

   if ( argc < 2 ) {
      _tprintf( TEXT("usage: \"%s\" <ServiceName>\n"), argv[0] );
      return;
   }

   __try {

      // Open the SCM database
      hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
      if ( !hSCM ) {
         DisplayError( TEXT("OpenSCManager()"), GetLastError() );
         __leave;
      }

      // Open the specified service
      hService = OpenService( hSCM, argv[1], SERVICE_STOP
            | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS );
      if ( !hService ) {
         DisplayError( TEXT("OpenService()"), GetLastError() );
         __leave;
      }

      // Try to stop the service, specifying a 30 second timeout
      dwError = StopService( hSCM, hService, TRUE, 30000 ) ;
      if ( dwError == ERROR_SUCCESS )
         _tprintf( TEXT("Service stopped.\n") );
      else
         DisplayError( TEXT("StopService()"), dwError );

   } __finally {

      if ( hService )
         CloseServiceHandle( hService );

      if ( hSCM )
         CloseServiceHandle( hSCM );
   }
} 

Additional query words:

Keywords : kbAPI kbKernBase kbOSWinNT400 kbOSWin2000 kbService kbOSWin98 kbSvcMgr kbDSupport kbGrpDSKernBase
Issue type : kbhowto
Technology : kbAudDeveloper kbWin32sSearch kbWin32API


Last Reviewed: October 23, 2000
© 2001 Microsoft Corporation. All rights reserved. Terms of Use.