My Training Period: xx hours. Before you begin, read some instruction here.
The Win32 programming skills:
Windows Services Story
There are two Windows service types:
A service application conforms to the interface rules of the Service Control Manager (SCM). It can be started:
Services can execute even when no user is logged on to the system. For normal applications such as your word processor program, we need to manually launch the program or automatically start it through the program Startup setting. A driver service conforms to the device driver protocols. It is similar to a service application, but it does not interact with the SCM. For simplicity, here, the term service refers to a service application. The following is the Services control panel that can be launched through Services menu:
Start → Programs → Administrative Tools → Services. To manage or control any service, you can select the service → right click your mouse → select Properties context menu.
You can also access the Services through the Control Panel: Start → Settings → Control Panel. Notice at the bottom of the Services snap-in there are Extended and Standard view. (Tenouk’s Windows XP Pro)
The following is a service property page for Windows Automatic Update service.
The running services (processes) can be seen through the Task Manager as shown in the following figure.
The SCM maintains a database of installed services and driver services, and provides a unified and secure means of controlling them. The database includes information on how each service or driver service should be started. It also enables system administrators to customize security requirements for each service and thereby control access to the service. The following types of programs use the functions provided by the SCM.
A program that provides executable code for one or more services. Service programs use functions that connect to the SCM and send status information to the SCM.
Service configuration program
A program that queries or modifies the services database. Service configuration programs use functions that open the database, install or delete services in the database, and query or modify the configuration and security parameters for installed services. Service configuration programs manage both services and driver services.
Service control program
A program that starts and controls services and driver services. Service control programs use functions that send requests to the SCM, which carries out the request.
The SCM is started at system boot. It is a remote procedure call (RPC) server, so that service configuration and service control programs can manipulate services on remote machines. The service functions provide an interface for the following tasks performed by the SCM:
Maintaining the database of installed services.
Starting services and driver services either upon system startup or upon demand.
Enumerating installed services and driver services.
Maintaining status information for running services and driver services.
Transmitting control requests to running services.
Locking and unlocking the service database.
The SCM maintains a database of installed services in the registry. The database is used by the SCM and programs that add, modify, or configure services. The following is the registry key for this database.
This key contains a subkey for each installed service and driver service. The name of the subkey is the name of the service, as specified by the CreateService() function when the service was installed by a service configuration program. An initial copy of the database is created when the system is installed. The database contains entries for the device drivers required during system boot. The database includes the following information about each installed service and driver service:
The service type. This indicates whether the service executes in its own process or shares a process with other services. For driver services, this indicates whether the service is a kernel driver or a file system driver.
The start type. This indicates whether the service or driver service is started automatically at system startup (auto-start service) or whether the SCM starts it when requested by a service control program (demand-start service). The start type can also indicate that the service or driver service is disabled, in which case it cannot be started.
The error control level. This specifies the severity of the error if the service or driver service fails to start during system startup and determines the action that the startup program will take.
The fully qualified path of the executable file. The filename extension is .exe for services and .sys for driver services.
Optional dependency information used to determine the proper order for starting services or driver services. For services, this information can include a list of services that the SCM must start before it can start the specified service, the name of a load ordering group that the service is part of, and a tag identifier that indicates the start order of the service in its load ordering group. For driver services, this information includes a list of drivers that must be started before the specified driver.
For services, an optional account name and password. The service program runs in the context of this account. If no account is specified, the service executes in the context of the LocalSystem account.
For driver services, an optional driver object name (for example, \FileSystem\Rdr or \Driver\Xns), used by the I/O system to load the device driver. If no name is specified, the I/O system creates a default name based on the driver service name.
Note: This database is also known as the ServicesActive database or the SCM database. You must use the functions provided by the SCM, instead of modifying the database directly.
During system boot, the SCM starts all auto-start services and the services on which they depend (service dependency). For example, if an auto-start service depends on another demand-start service, the demand-start service is also started automatically. The load order is determined by the following:
The order of groups in the load ordering group list, ServiceGroupOrder, in the following registry key:
The order of services within a group specified in the tags order vector, GroupOrderList, in the following registry key:
The dependencies listed for each service.
When the boot is complete, the system executes the boot verification program specified by BootVerificationProgram value of the following registry key:
By default, this value is not set. The system simply reports that the boot was successful after the first user has logged on. You can supply a boot verification program that checks the system for problems and reports the boot status to the SCM using the NotifyBootConfigStatus() function. After a successful boot, the system saves a clone of the database in the last-known-good (LKG) configuration. The system can restore this copy of the database if changes made to the active database cause the system reboot to fail. The following is the registry key for this database:
Where XXX is the value saved in the following registry key value:
If an auto-start service with a SERVICE_ERROR_CRITICAL error control level fails to start, the SCM reboots the machine using the LKG configuration. If the LKG configuration is already being used, the boot fails.
Starting Services on Demand
The user can start a service with the Services control panel utility. The user can specify arguments for the service in the Start parameters field. A service control program can start a service and specify its arguments with the StartService() function. When the service is started, the SCM performs the following steps:
The user can stop a service with the Services control panel utility. A service control program can stop a service with the ControlService() function, 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 services that are running are dependent on the specified service, it does not forward the stop request. Instead, it returns the ERROR_DEPENDENT_SERVICES_RUNNING error code. To programmatically stop a service with dependent services, you must first enumerate and stop its dependent services.
As each service entry is read from the database of installed services, the SCM creates a service record for the service. A service record includes:
Start type (auto-start or demand-start).
Service status (see the SERVICE_STATUS structure)
Acceptable control codes.
Pointer to dependency list.
The user name and password of an account are specified at the time the service is installed. The SCM stores the user name in the registry and the password in a secure portion of the Local Security Authority (LSA). The system administrator can create accounts with passwords that never expire. Alternatively, the system administrator can create accounts with passwords that expire and manage the accounts by changing the passwords periodically. The SCM keeps two copies of a user account's password, a current password and a backup password. The password specified the first time the service is installed is stored as the current password and the backup password is not initialized. When the SCM attempts to run the service in the security context of the user account, it uses the current password. If the current password is used successfully, it is also saved as the backup password. If the password is modified with the ChangeServiceConfig() function, or the Services control panel utility, the new password is stored as the current password and the previous password is stored as the backup password. If the SCM attempts to start the service and the current password fails, then it uses the backup password. If the backup password is used successfully, it is saved as the current password. The SCM updates the service status when a service sends it status notifications using the SetServiceStatus() function. The SCM maintains the status of a driver service by querying the I/O system, instead of receiving status notifications, as it does from a service. A service can register additional type information by calling the SetServiceBits() function. The NetServerGetInfo() and NetServerEnum() functions obtain the supported service types.
The SCM supports handle types to allow access to the following objects.
The database of installed services.
The database lock.
An SCManager object represents the database of installed services. It is a container object that holds service objects. The OpenSCManager() function returns a handle to an SCManager object on a specified computer. This handle is used when installing, deleting, opening, and enumerating services and when locking the services database. A service object represents an installed service. The CreateService() and OpenService() functions return handles to installed services. The OpenSCManager(), CreateService(), and OpenService() functions can request different types of access to SCManager and service objects. The requested access is granted or denied depending on the access token of the calling process and the security descriptor associated with the SCManager or service object.
The CloseServiceHandle() function closes handles to SCManager and service objects. When you no longer need these handles, be sure to close them. A lock object is created during SCM initialization to serialize access to the database of installed services. The SCM acquires the lock before starting a service or driver service. Service configuration programs use the LockServiceDatabase() function to acquire the lock before reconfiguring a service and use the UnlockServiceDatabase() function to release the lock.
A service program contains executable code for one or more services. A service created with the type SERVICE_WIN32_OWN_PROCESS only contains the code for one service. A service created with the type SERVICE_WIN32_SHARE_PROCESS contains code for more than one service, enabling them to share code. A service can be configured to execute in the context of a user account from either the built-in (local), primary, or trusted domain. It can also be configured to run in a special service user account. The following topics describe the interface requirements of the SCM that a service program must include and additional programming considerations. These sections do not apply to driver services.
Service Entry Point.
Service ServiceMain() Function
Service Control Handler Function.
Services are generally written as console applications. The entry point of a console application is its main function. The main function receives arguments from the ImagePath value from the registry key for the service. When the SCM starts a service program, it waits for it to call the StartServiceCtrlDispatcher() function. You can use the following guidelines.
A service of type SERVICE_WIN32_OWN_PROCESS should call StartServiceCtrlDispatcher() immediately, from its main thread. You can perform any initialization after the service starts.
If the service type is SERVICE_WIN32_SHARE_PROCESS and there is common initialization for all services in the program, you can perform the initialization in the main thread before calling StartServiceCtrlDispatcher(), as long as it takes less than 30 seconds. Otherwise, you must create another thread to do the common initialization, while the main thread calls StartServiceCtrlDispatcher(). You should still perform any service-specific initialization after the service starts.
The StartServiceCtrlDispatcher() function takes a SERVICE_TABLE_ENTRY structure for each service contained in the process. Each structure specifies the service name and the entry point for the service. If StartServiceCtrlDispatcher() succeeds, the calling thread does not return until all running services in the process have terminated. The SCM sends control requests to this thread through a named pipe. The thread acts as a control dispatcher, performing the following tasks:
Create a new thread to call the appropriate entry point when a new service is started.
Call the appropriate handler function to handle service control requests.
When a service control program requests that a new service run, the SCM starts the service and sends a start request to the control dispatcher. The control dispatcher creates a new thread to execute the ServiceMain() function for the service. The ServiceMain() function should perform the following tasks:
Call the RegisterServiceCtrlHandlerEx() function immediately to register a HandlerEx() function to handle control requests for the service. The return value of RegisterServiceCtrlHandlerEx() is a service status handle that will be used in calls to notify the SCM of the service status.
Perform initialization. If the execution time of the initialization code is expected to be very short (less than one second), initialization can be performed directly in ServiceMain().
If the initialization time is expected to be longer than one second, call the SetServiceStatus() function, specifying the SERVICE_START_PENDING service state and a wait hint in the SERVICE_STATUS structure. As initialization continues, the service should make additional calls to SetServiceStatus() to report progress. Sending multiple SetServiceStatus() calls is useful for debugging services.
When initialization is complete, call SetServiceStatus(), specifying the SERVICE_RUNNING state in the SERVICE_STATUS structure.
Perform the service tasks, or, if there are no pending tasks, return. Any change in the state of the service warrants a call to SetServiceStatus() to report new status information.
If an error occurs while the service is initializing or running, the service should call SetServiceStatus(), specifying the SERVICE_STOP_PENDING state in the SERVICE_STATUS structure, if cleanup will be lengthy. Once cleanup is complete, call SetServiceStatus() from the last thread to terminate, specifying SERVICE_STOPPED in the SERVICE_STATUS structure. Be sure to set the dwServiceSpecificExitCode and dwWin32ExitCode members of the SERVICE_STATUS structure to identify the error.
Each service has a control handler, the HandlerEx() function, that is invoked by the control dispatcher when the service process receives a control request from a service control program. Therefore, this function executes in the context of the control dispatcher. Whenever HandlerEx() is invoked, the service must call the SetServiceStatus() function to report its status to the SCM. This must be done regardless of whether the status changed. The service control program can send control requests using the ControlService() function. All services must accept and process the SERVICE_CONTROL_INTERROGATE control code. You can enable or disable acceptance of the other control codes by calling SetServiceStatus(). To receive the SERVICE_CONTROL_DEVICEEVENT control code, you must call the RegisterDeviceNotification() function. Services can also handle additional user-defined control codes.
The control handler must return within 30 seconds, or the SCM will return an error. If a service needs to do lengthy processing when the service is executing the control handler, it should create a secondary thread to perform the lengthy processing, and then return. This prevents the service from tying up the control dispatcher. For example, when handling the stop request for a service that will take a long time, create another thread to handle the stop process. The control handler should simply call SetServiceStatus() with the SERVICE_STOP_PENDING message and return.
When the user shuts down the system, all control handlers that have called SetServiceStatus() with the SERVICE_ACCEPT_SHUTDOWN control code receive the SERVICE_CONTROL_SHUTDOWN control code. They are notified in the order that they appear in the database of installed services. By default, a service has approximately 20 seconds to perform cleanup tasks before the system shuts down. After this time expires, system shutdown proceeds regardless of whether service shutdown is complete. Note that if the system is left in the shutdown state (not restarted or powered down), the service continues to run.
If the service needs more time to clean up, it sends STOP_PENDING status messages, along with a wait hint, so the service controller knows how long to wait before reporting to the system that service shutdown is complete. However, there is a limit to how long the service controller will wait, to prevent a service from stopping shutdown. To change this time limit, modify the WaitToKillServiceTimeout value in the following registry key:
Note that during service shutdown, the SCM does not take dependencies into consideration. The SCM enumerates the list of running services and sends the SERVICE_CONTROL_SHUTDOWN command. Therefore, a service may fail because another service it depends on has already stopped.
A service that is a console application can register a console control handler to receive notification when a user logs off. However, there is no event sent when an interactive user logs on. To determine whether an interactive user is logged on, verify that the process specified in the following key is running:
This process is started when an interactive user logs onto a system. Therefore, if this process is running, there is an interactive user logged on. The system broadcasts device change events to all services. These events can be received by a service in a window procedure or in its service control handler. To specify which events your service should receive, use the RegisterDeviceNotification() function.
Be sure to handle Plug and Play device events as quickly as possible. Otherwise, the system may become unresponsive. If your event handler is to perform an operation that may block execution (such as I/O), it is best to start another thread to perform the operation asynchronously. When a service calls RegisterDeviceNotification(), the service also specifies either a window handle or a service status handle. If a service specifies a window handle, the window procedure receives the notification events. If a service specifies its service status handle, its service control handler receives the notification events. Device notification handles returned by RegisterDeviceNotification() must be closed by calling the UnregisterDeviceNotification() function when they are no longer needed.
Further reading and digging:
Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.
Windows data types are Windows data types.
Microsoft Visual C++, online MSDN.