|< Windows Services Programming 4 | Main | Windows Services Programming 6 >| Site Index | Download |


 

 

 

 

MODULE EE2

WINDOWS SERVICES

Part 5: MORE PROGRAM EXAMPLES

 

 

 

 

 

 

 

 

 

 

 

What do we have in this Module?

  1. Starting a Windows service program example.

  2. Stopping a Windows service program example.

 

My Training Period:  xx hours. Before you begin, read some instruction here.

 

 

The programming abilities:

  • Able to collect and understand all the required information related to Windows Services.

  • Able to understand, build and run various Windows Service application programs.

Note: For Multithreaded program examples, you have to set your project to Multithread project type.

 

Starting a Service

 

To start a service, the following example opens a handle to an installed database and then specifies the handle in a call to the StartService() function. It can be used to start either a service or a driver service, but this example assumes that a service is being started. After starting the service, the program uses the members of the SERVICE_STATUS structure returned by the QueryServiceStatus() function to track the progress of the service. The function requires a handle to a service control manager database. For more information, see Opening an SCManager Database example.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

BOOL StartSampleService(SC_HANDLE);

 

int main()

{

       SC_HANDLE schSCManager;

       // Open a handle to the SC Manager database...

       schSCManager = OpenSCManager(

       NULL,                    // local machine

       NULL,                    // SERVICES_ACTIVE_DATABASE database is opened by default

       SC_MANAGER_ALL_ACCESS);  // full access rights

 

if (NULL == schSCManager)

       printf("OpenSCManager() failed, error: %d.\n", GetLastError());

else

       printf("OpenSCManager() looks OK.\n");

       // Call the StartSampleService()...

       StartSampleService(schSCManager);

       return 0;

}

 

BOOL StartSampleService(SC_HANDLE schSCManager)

{

    SC_HANDLE schService;

    SERVICE_STATUS ssStatus;

    DWORD dwOldCheckPoint;

    DWORD dwStartTickCount;

    DWORD dwWaitTime;

       // You can try to accept the service name through the command line...

       // To test this program, make sure the dhcp client is installed and

       // in the stop state...

       LPCTSTR lpszServiceName = "dhcp";

       schService = OpenService(

        schSCManager,          // SCM database

        lpszServiceName,       // service name

        SERVICE_ALL_ACCESS);

    if (schService == NULL)

    {

       printf("OpenService() failed, error: %d.\n", GetLastError());

       return 0;

    }

   else

       printf("OpenService() looks OK.\n");

       // Proceed to other task...

    if (!StartService(

            schService,  // handle to service

            0,                  // number of arguments

            NULL) )       // no arguments

    {

        printf("StartService() failed, error: %d.\n", GetLastError());

              return 0;

    }

    else

        printf("StartService(), service start pending.\n");

    // Check the status until the service is no longer start pending.

    if (!QueryServiceStatus(

            schService,   // handle to service

            &ssStatus))   // address of status information structure

    {

        printf("StartService(), service still start pending.\n");

       return 0;

    }

    else

       printf("StartService(), service no longer start pending.\n");

    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();

    dwOldCheckPoint = ssStatus.dwCheckPoint;

    while (ssStatus.dwCurrentState == SERVICE_START_PENDING)

    {

        // Just some info...

        printf("Wait Hint: %d\n", ssStatus.dwWaitHint);

        // Do not wait longer than the wait hint. A good interval is

        // one tenth the wait hint, but no less than 1 second and no

        // more than 10 seconds...

        dwWaitTime = ssStatus.dwWaitHint / 10;

        if (dwWaitTime < 1000)

            dwWaitTime = 1000;

        else if (dwWaitTime > 10000)

            dwWaitTime = 10000;

        Sleep(dwWaitTime);

        // Check the status again...

        if (!QueryServiceStatus(

                schService,   // handle to service

                &ssStatus))   // address of structure

            break;

        if (ssStatus.dwCheckPoint > dwOldCheckPoint)

        {

            // The service is making progress...

            printf("Service starting in progress...\n");

            dwStartTickCount = GetTickCount();

            dwOldCheckPoint = ssStatus.dwCheckPoint;

        }

        else

        {

            if ((GetTickCount()-dwStartTickCount) > ssStatus.dwWaitHint)

            {

                // No progress made within the wait hint

                printf("Well, starting the service looks no progress...\n");

                break;

            }

        }

    }

    if (CloseServiceHandle(schService) == 0)

       printf("CloseServiceHandle() failed, error: %d.\n", GetLastError());

    else

       printf("CloseServiceHandle() looks OK.\n");

    if (ssStatus.dwCurrentState == SERVICE_RUNNING)

    {

        printf("StartService(), %s service successfully started.\n", lpszServiceName);

        return 1;

    }

    else

    {

        printf("\nService %s not started.\n", lpszServiceName);

        printf("  Current State: %d\n", ssStatus.dwCurrentState);

        printf("  Exit Code: %d\n", ssStatus.dwWin32ExitCode);

        printf("  Service Specific Exit Code: %d\n", ssStatus.dwServiceSpecificExitCode);

        printf("  Check Point: %d\n", ssStatus.dwCheckPoint);

        printf("  Wait Hint: %d\n", ssStatus.dwWaitHint);

        return 0;

    }

}

 

Windows services: Starting a service program example

 

Then, run the program example.

 

Windows services: Starting a service program example

 

Verify again through Service Control panel (also can be verified through Task Manager)

 

Windows services: Starting a service verification through Services snap-in

 

Another Example: Starting Task Scheduler

 

This example tests to see which operating system is being used, then checks to see if the Task Scheduler service is running. It starts the service if it is not already running.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <Windows.h>

#include <stdio.h>

 

#define SCHED_CLASS             TEXT("SAGEWINDOWCLASS")

#define SCHED_TITLE                TEXT("SYSTEM AGENT COM WINDOW")

#define SCHED_SERVICE_APP_NAME      TEXT("mstask.exe")

#define SCHED_SERVICE_NAME      TEXT("Schedule")

 

int main(int argc, char **argv)

{

  OSVERSIONINFO osver;

  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

  // Determine which version of OS you are running.

  GetVersionEx(&osver);

  if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)

  {

    // If Windows 95, check to see if Windows 95 version of Task Scheduler is running.

    HWND hwnd = FindWindow(SCHED_CLASS, SCHED_TITLE);

    if (hwnd != NULL)

    {

      // It is already running.

      printf("Task Scheduler is already running.\n");

      return 0;

    }

    // Execute the task scheduler process.

    STARTUPINFO         sui;

    PROCESS_INFORMATION pi;

    ZeroMemory(&sui, sizeof(sui));

    sui.cb = sizeof (STARTUPINFO);

    TCHAR szApp[MAX_PATH];

    LPTSTR pszPath;

    DWORD dwRet = SearchPath(NULL,

                             SCHED_SERVICE_APP_NAME,

                             NULL,

                             MAX_PATH,

                             szApp,

                             &pszPath);

    if (dwRet == 0)

        { return GetLastError(); }

    else

        printf("SearchPath() is OK.\n");

    BOOL fRet = CreateProcess(szApp,

               NULL,

               NULL,

               NULL,

               FALSE,

               CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,

               NULL,

               NULL,

               &sui,

               &pi);

    if (fRet == 0)

    {

      return GetLastError();

    }

    else

       printf("CreateProcess() is OK.\n");

    CloseHandle(pi.hProcess);

    CloseHandle(pi.hThread);

    return S_OK;

  }

  else

  {

    // If not Windows 95, check to see if Windows NT version of Task Scheduler is running.

    SC_HANDLE   hSC = NULL;

    SC_HANDLE   hSchSvc = NULL;

    hSC = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

    if (hSC == NULL)

    {

      return GetLastError();

    }

    else

       printf("OpenSCManager() is OK.\n");

    hSchSvc = OpenService(hSC, SCHED_SERVICE_NAME, SERVICE_START | SERVICE_QUERY_STATUS);

    CloseServiceHandle(hSC);

    if (hSchSvc == NULL)

    {

      return GetLastError();

    }

    else

       printf("OpenService() is OK.\n");

    SERVICE_STATUS SvcStatus;

    if (QueryServiceStatus(hSchSvc, &SvcStatus) == FALSE)

    {

      CloseServiceHandle(hSchSvc);

      return GetLastError();

    }

    else

       printf("QueryServiceStatus() is OK.\n");

    if (SvcStatus.dwCurrentState == SERVICE_RUNNING)

    {

      // The service is already running.

      CloseServiceHandle(hSchSvc);

      printf("Task Scheduler is already running.\n");

      return 0;

    }

    if (StartService(hSchSvc, 0, NULL) == FALSE)

    {

      CloseServiceHandle(hSchSvc);

      printf("Could not start Task Scheduler.\n");

      return GetLastError();

    }

    CloseServiceHandle(hSchSvc);

    printf("Task Scheduler has been started.\n");

    return S_OK;

  }

}

 

Windows services: Starting a task scheduler program example

 

Stopping a Service

 

A service can be stopped with the ControlService() function by sending a SERVICE_CONTROL_STOP request. If the SCM receives a SERVICE_CONTROL_STOP request for a service, it instructs the service to stop by forwarding the request 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_DEPENDENT_SERVICES_RUNNING. Therefore, to programmatically stop such a service, you must first enumerate and stop its dependent services. The following code implements a StopService() function, which optionally attempts to stop the specified service's dependent services.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

 

DWORD StopService(SC_HANDLE, SC_HANDLE, BOOL, DWORD);

void DisplayError(LPTSTR, DWORD);

// 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

// Using _tmain(), TCHAR - A WCHAR if UNICODE is defined, a CHAR otherwise.

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

{

   SC_HANDLE hSCM;

   SC_HANDLE hService;

   DWORD     dwError;

   // If no command line argument supplied...

   if (argc < 2)

   {

      _tprintf(TEXT("usage: %s <ServiceName>\n"), argv[0]);

      _tprintf(TEXT("Please try again.\n"));

      return;

   }

   // Exception handling...

   __try

   {

      // Open the SCM database

      hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);

      if (!hSCM)

      {

         DisplayError(TEXT("OpenSCManager() failed.\n"), GetLastError());

         __leave;

      }

      else

       _tprintf(TEXT("OpenSCManager() looks OK.\n"));

      // Open the specified service

      hService = OpenService(hSCM, argv[1], SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS);

      if (!hService)

      {

         DisplayError(TEXT("OpenService() failed.\n"), GetLastError());

         __leave;

      }

      else

         _tprintf(TEXT("OpenService() looks OK.\n"));

      // Try to stop the service, specifying a 40 second timeout

      dwError = StopService(hSCM, hService, TRUE, 40000);

      if (dwError == ERROR_SUCCESS)

         _tprintf(TEXT("The %s service was successfully stopped.\n"), argv[1]);

      else

         DisplayError(TEXT("StopService(), stopping the service failed.\n"), dwError);

   }

   __finally

   {

      if (hService)

         CloseServiceHandle(hService);

      if (hSCM)

         CloseServiceHandle(hSCM);

   }

}

 

// This function attempts to stop a service. It allows the caller to

// specify whether dependent services should also be stopped. It also

// accepts a timeout value, to prevent a scenario in which a service

// shutdown hangs, then the application stopping the service hangs.

//

// Parameters:

//   hSCM - Handle to the service control manager.

//   hService - Handle to the service to be stopped.

//   fStopDependencies - Indicates whether to stop dependent services.

//   dwTimeout - maximum time (in milliseconds) to wait

//

// If the operation is successful, returns ERROR_SUCCESS. Otherwise, returns a system error code.

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

   else

       _tprintf(TEXT("QueryServiceStatus(), querying the service status...\n"));

   if (ss.dwCurrentState == SERVICE_STOPPED)

   {

       _tprintf(TEXT("Service already stopped.\n"));

       return ERROR_SUCCESS;

    }

 

  // If a stop is pending, just wait for it..

   _tprintf(TEXT("Checking the service pending status...\n"));

   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)) != 0)

      {

          _tprintf(TEXT("EnumDependentServices(), no dependencies lol!\n"));

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

      }

      else

      {

         _tprintf(TEXT("EnumDependentServices(), there are dependencies lol!\n"));

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

              _tprintf(TEXT("The number of dependencies: %d.\n"), dwCount);

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

               {

              ess = *(lpDependencies + i);

              _tprintf(TEXT("Dependencies #%d: %s.\n"), i, ess.lpServiceName);

               // Open the service

               hDepService = OpenService(hSCM, ess.lpServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS);

               if (!hDepService)

                  return GetLastError();

               __try {

                     _tprintf(TEXT("Stopping dependency #%d.\n"), i);

                       // 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(10000);

                     //Sleep(ss.dwWaitHint);

                     if (!QueryServiceStatus(hDepService, &ss))

                        return GetLastError();

                     if (ss.dwCurrentState == SERVICE_STOPPED)

                         _tprintf(TEXT("the %s service was stopped...\n"), ess.lpServiceName);

                         // Dependency was stopped

                     break;

                     if (GetTickCount() - dwStartTime > dwTimeout)

                        return ERROR_TIMEOUT;

                  }

               }

               __finally

               {

                  // Always release the service handle

                  if (CloseServiceHandle(hDepService) == 0)

                    _tprintf(TEXT("CloseServiceHandle() failed, error: %d.\n"), GetLastError());

                 else

                    _tprintf(TEXT("CloseServiceHandle() looks OK.\n"));

               }

            }

         }

         __finally

         {

            // Always free the enumeration buffer

            if (HeapFree(GetProcessHeap(), 0, lpDependencies) == 0)

              _tprintf(TEXT("HeapFree() failed, error: %d.\n"), GetLastError());

            else

              _tprintf(TEXT("HeapFree() looks OK.\n"));

         }

      }

   }

   // Assuming all dependencies already stopped, 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(10000);

      // Sleep(ss.dwWaitHint);

      if (!QueryServiceStatus(hService, &ss))

         return GetLastError();

      if (ss.dwCurrentState == SERVICE_STOPPED)

      // The main service was stopped...

         break;

      if (GetTickCount() - dwStartTime > dwTimeout)

         return ERROR_TIMEOUT;

   }

 

   // Return success

   return ERROR_SUCCESS;

}

// Helper function to display an error message

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

   if (LocalFree(lpBuffer) == NULL)

     _tprintf(TEXT("LocalFree() looks OK.\n"));

   else

     _tprintf(TEXT("LocalFree() failed.\n"));

}

 

In this example we are going to stop IISADMIN service. This service has one dependency that is W3SVC as shown below. Make sure the services already started.

 

Windows services: Stopping a services program example

 

A sample program output is shown  below.

 

Windows services: Stopping a service program example output.

 

Well, you can verify through Service Control panel or Task Manager whether both services were stopped. Tenouk has tried stopping other services on the Windows XP Pro and 2000 server that having many dependencies; the output varies with several error codes. It is left for you to debug :o)

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.

  2. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).

  3. Windows data types are Windows data types.

  4. Microsoft Visual C++, online MSDN.

  5. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

 

|< Windows Services Programming 4 | Main | Windows Services Programming 6 >| Site Index | Download |