Writing a Service Program

Environment: Visual C++6 SP5, Windows 2000/XP

Introduction

A few weeks ago, I was trying to make my own server application into a service without much knowledge about how to do it. Now, I know more about services and would like to share my knowledge in this article.

Services are processes that can be auto started when the system boots, and can be left them running while the system is on. For example, system services are services such as an Event log that logs messages issued by Windows-based programs and components to be viewed in Event Viewer. Services are similar to UNIX's daemons.

A Windows service applet can be found in Control Panel > Administrator Tools > Services. Any new services installation creates an entry in this applet and in the Registry (HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services), so that the system knows about it each time it boots.

Every service must be able to respond to a set of standard events that the services applet can pass to it. These events are shown in the window service as buttons:

  • START: Starts the service if it is a manual start service or it is stopped.
  • STOP: Stops the service.
  • PAUSE: Suspends the service temporarily.
  • CONTINUE: Resumes the suspended service.

All services are managed under a system called the Service Control Manager. It maintains the list of known services in the Registry and starts them at boot time or when requested.

A program that acts as a service is a normal EXE file, but it must meet special requirements so that it interfaces properly with the Service Control Manager (SCM). The requirements follow:

  1. EXE must have a normal main or WinMain function. Call the StartServiceCrtlDispatcher function to register the EXE with the SCM and give the SCM a pointer to the ServiceMain function.
  2. void main(int argc, char *argv[])
    {
      SERVICE_TABLE_ENTRY serviceTable[] =
      {
        { ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
          { NULL, NULL}
      };
      BOOL success;
    
      if(argc == 2)
        //..check arguments
      else
      {
        //register with SCM
        success = StartServiceCtrlDispatcher(serviceTable);
        if (!success)
          ErrorHandler("StartServiceCtrlDispatcher",
                        GetLastError());
      }
    }
    
  3. SCM will call the ServiceMain function when it wants to start the service. The ServiceMain should immediately call the RegisterServiceCtrlHandler function to register a Handler function with SCM.
  4. void ServiceMain(DWORD argc, LPTSTR *argv)
    {
      BOOL success;
    
      //immediately call registration function
      serviceStatusHandle =
             RegisterServiceCtrlHandler(ServiceName,
             (LPHANDLER_FUNCTION)ServiceCtrlHandler);
    
      //notify SCM
      //create termination event
      terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
      .
      .
      //notify SCM
      .
      .
      //check for startup parameter
      //start service
      //notify SCM service is runnning
      SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
    
      //wait for stop signal and then terminate
      WaitForSingleObject(terminateEvent, INFINITE);
    
      terminate(0);
    }
    
  5. The Handler function contains switch statements that parse control requests received from the SCM.
  6. void ServiceCtrlHandler(DWORD controlCode)
    {
      DWORD currentState = 0;
      BOOL success;
    
      switch(controlCode)
      {
        // START = ServiceMain()
    
        // STOP
        case SERVICE_CONTROL_STOP:
          currentState = SERVICE_STOP_PENDING;
          //notify SCM
          SendStatusToSCM(SERVICE_STOP_PENDING,
            NO_ERROR,
            0,
            1,
            5000);
          //stop service
          StopService();
          return;
    
        // PAUSE
        case SERVICE_CONTROL_PAUSE:
          if (runningService && !pauseService)
          {
            //notify SCM
            success = SendStatusToSCM(
              SERVICE_PAUSE_PENDING,
              NO_ERROR,
              0,
              1,
              1000);
    
            PauseService();
            currentState = SERVICE_PAUSED;
          }
          break;
    
        // RESUME
        case SERVICE_CONTROL_CONTINUE:
          if (runningService && pauseService)
          {
            //notify SCM
            success = SendStatusToSCM(
              SERVICE_CONTINUE_PENDING,
              NO_ERROR,
              0,
              1,
              1000);
    
            ResumeService();
            currentState = SERVICE_RUNNING;
          }
          break;
    
        // UPDATE
        case SERVICE_CONTROL_INTERROGATE:
          //update status out of switch()
          break;
    
        case SERVICE_CONTROL_SHUTDOWN:
          //do nothing
          return;
        default:
          break;
      }
      //notify SCM current state
      SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
    }
    
  7. When you create an EXE that contains that main, ServiceMain, and Handler functions, as well as a function that contains the thread for the service itself, you will have a complete service program.

Template for the Service Program

The accompanying .cpp file is a template for the service program, service control program (RUN, STOP, PAUSE, CONTINUE), and also the configuration program in the sense that it can install or remove the EXE itself using command.

Usage

  1. Add service.cpp to the project workspace.
  2. Change the ServiceName under the global variable to the desired name.
  3. Add your function into ServiceThread().
  4. DONE!

To Install

  1. Use command prompt.
  2. Locate the EXE.
  3. Type <programname> -i to install
    Type <programname> -u to uninstall
    Type <programname> -r to run
    Type <programname> -s to stop
    Type <programname> -p to pause
    Type <programname> -c to continue
    Type <programname> help

Downloads

Download source - 6 Kb