Windows Services

Almost every operating system has a mechanism to start processes at system startup time that provide services not tied to an interactive user. In Windows, such processes are called services or Windows services. Windows services consist of three components: a service application, a service control program (SCP), and the service control manager (SCM).

Service applications, such as Web servers, consist of at least one executable that runs as a Windows service. Service applications are simply Windows executables (GUI or console, most without an interface) with additional code to receive commands from the SCM as well as to communicate the application's status back to the SCM. A user wanting to start, stop, or configure a service uses an SCP.

When you install an application that includes a service, the application's setup program must register the service with the system. A service configuration program uses the CreateService function to install a service in a SCM database. Here is an example of how to install a service:

 


 #include <windows.h><br>#include <stdio.h><br>BOOL CreateSampleService() <br>{ <br>    TCHAR szPath[MAX_PATH];         if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )<br>    {<br>        printf("GetModuleFileName failed (%d)\n", GetLastError()); <br>        return FALSE;<br>    }     schService = CreateService( <br>        schSCManager,              // SCManager database <br>        TEXT("Sample_Srv"),        // name of service <br>        lpszDisplayName,           // service name to display <br>        SERVICE_ALL_ACCESS,        // desired access <br>        SERVICE_WIN32_OWN_PROCESS, // service type <br>        SERVICE_DEMAND_START,      // start type <br>        SERVICE_ERROR_NORMAL,      // error control type <br>        szPath,                    // path to service's binary <br>        NULL,                      // no load ordering group <br>        NULL,                      // no tag identifier <br>        NULL,                      // no dependencies <br>        NULL,                      // LocalSystem account <br>        NULL);                     // no password      if (schService == NULL) <br>    {<br>        printf("CreateService failed (%d)\n", GetLastError()); <br>        return FALSE;<br>    }<br>    else<br>    {<br>        CloseServiceHandle(schService); <br>        return TRUE;<br>    }<br>} 


When a setup program registers a service by calling CreateService, a message is sent to the SCM on the machine where the service will reside. The SCM then creates a registry key for the service under HKLM\SYSTEM\CurrentControlSet\Services. The Services key is the nonvolatile representation of the SCM's database.

Because most services don't have a user interface, they are built as console programs. The entry point of a console application is its main function. When the SCM starts a service program, it waits for it to call the StartServiceCtrlDispatcher function. So the main function of a service program calls the StartServiceCtrlDispatcher function to connect to the SCM and start the control dispatcher thread. StartServiceCtrlDispatcher accepts a list of entry points into services, one entry point for each service in the process. Each entry point is identified by the name of the service the entry point corresponds to. The dispatcher thread loops, waiting for incoming control requests for the services specified in the dispatch table. Here is an example of Writing a Service Program's main Function.

The SCM sends a service-start command each time it starts a service the process owns. For each start command it receives, the StartServiceCtrlDispatcher function creates a thread, called a service thread, to invoke the starting service's entry point and implement the command loop for the service. StartServiceCtrlDispatcher waits indefinitely for commands from the SCM and returns control to the process's main function only when all the process's services have stopped, allowing the service process to clean up resources before exiting.

A service entry point's first action is to call the RegisterServiceCtrlHandler function. This function receives and stores a pointer to a function, called the control handler, which the service implements to handle various commands it receives from the SCM. RegisterServiceCtrlHandler doesn't communicate with the SCM, but it stores the function in local process memory for the StartServiceCtrlDispatcher function. The service entry point continues initializing the service, which can include allocating memory, creating communications end points, and reading private configuration data from the registry. A convention most services follow is to store their parameters under a subkey of their service registry key, named Parameters. While the entry point is initializing the service, it might periodically send status messages, using the SetServiceStatus function, to the SCM indicating how the service's startup is progressing. After the entry point finishes initialization, a service thread usually sits in a loop waiting for requests from client applications. For example, a Web server would initialize a TCP listen socket and wait for inbound HTTP connection requests.

A service process's main thread, which executes in the StartServiceCtrlDispatcher function, receives SCM commands directed at services in the process and invokes the target service's control handler function (stored by RegisterServiceCtrlHandler). SCM commands include stop, pause, resume, interrogate, and shutdown, or application-defined commands.

Inside a service process

This figure shows the internal organization of a service process. Pictured are the two threads that make up a process hosting one service: the main thread and the service thread.

The following is a simplified overview of what happens when a typical service is started by the service control manager:

  • The SCM reads the service path from the registry and prepares to start the service. This includes acquiring the service lock.
  • The SCM starts the process and waits until either the child process exits (indicating a failure) or reports the SERVICE_RUNNING status.
  • The application performs its very simple initialization and calls the StartServiceCtrlDispatcher function.
  • StartServiceCtrlDispatcher connects to the service control manager and starts a second thread that calls the ServiceMain function for the service. ServiceMain should report SERVICE_RUNNING as soon as possible.
  • When the service control manager is notified that the service is running, it releases the service lock.

If you have a program that you want to run as a service, you need to modify the startup code to conform to the requirements for services. If you don't have the source code, you can use the SrvAny tool. SrvAny and its installer "InstSrv" are applications provided by Microsoft. SrvAny allows Windows applications to run as a service.

By default Windows services are run under the virtual user "LocalService" that has administrative rights on the system. When a service runs as LocalService, Windows allows the user to configure it so it is "allowed to interact with desktop" (that is, display user-visible dialog boxes and other windows). However, if running as any other user, this option is not available.

Service Changes for Windows Vista

There have been significant changes to the services model to improve performance, reliability, security, management, and administration of services. These changes include:

  • Delayed Auto-Start: Delayed auto-start services are started shortly after the system has started. This improves system startup performance while still providing automatic startup for these services.
  • Restricted Network Access: You can use service SIDs to restrict access to ports, protocols, or the direction of network traffic.
  • Session 0 Isolation: Services have always run in session 0. Before Windows Vista, the first user to log on was also assigned to session 0. Now, session 0 is reserved exclusively for services and other applications not associated with an interactive user session. (The first user to log on is connected to session 1, the second user to log on is connected to session 2, and so on.). Session 0 does not support processes that interact with the user.

References: