Geoff Chappell, Software Analyst
Services, being an important layer of low-level user-mode code, have long been documented by Microsoft even though relatively few programmers ever write a service. At their simplest, each service is its own executable to run as its own process. More generally, services may be packaged together so that they run in the one process and can benefit from sharing code. Note that there is no requirement that the services in the one process must be coded in the one executable. Especially if each service is substantial, or just if you care about modularisation, you might code each service in its own DLL.
This is indeed what Microsoft does for many services that are supplied with Windows. Instead of each being its own service program, each is instead a service DLL that executes in a service-hosting process. The original host was the Service Control Manager itself, just for a set of services whose names were hard-coded into the SERVICES.EXE program. Windows 2000 generalised the machinery, introducing a program named SVCHOST.EXE which exists only to host services. At any given time there can be multiple instances of SVCHOST, each running its own group of services. Indeed, successive versions of Windows have ever more such instances.
Since much of the sort of background activity that puzzles users and troubles administrators is done by services in these instances of SVCHOST, it may surprise that Microsoft publishes very little about SVCHOST. The Help and Support in Windows 2000 doesn’t give SVCHOST a single mention, but Microsoft obviously was asked about SVCHOST enough (and by the right people) to prompt a Knowledge Base article, Description of Svchost.exe in Windows 2000, and there is nowadays a page What is svchost.exe? in the Windows Help and Support. Most of the relevant registry parameters, such as system administrators typically like to know about (if just for assurance), seem still to be undocumented. As for the interfaces between SVCHOST and its service DLLs, such as might be useful to a non-Microsoft programmer writing services, the documentation says next to nothing and arguably only then by oversight.
As with any service program, SVCHOST expects to run from a command line in the registry, specifically from the ImagePath value of some service’s subkey of HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services. The intended syntax for a SVCHOST command line looks to be
[path\]svchost.exe -k group
Straightforward variations are that:
Other variations—indeed, quirks—may be unintended and seem anyway to have no consequence in real-world practice.
The point to the group argument is to specify indirectly which services will run as service DLLs in this one SVCHOST instance. The list of services that constitute the specified group is found through the registry:
Each string in the multi-string data names a service that is implemented as a service DLL. It is surely intended, but is not checked, that each such service should have its ImagePath set to SVCHOST for the same group.
With (similar) services executing in the same process, initialisation that each service might otherwise do for itself, each reproducing the same code, is instead done once for the whole process. A few points of this SVCHOST process initialisation are separately configurable for each SVCHOST instance. The relevant parameters, which are all optional, are found in the registry by treating group as a subkey.
When a hosted service is to be started, SVCHOST loads the corresponding service DLL. Details of this DLL are configurable through the service’s registry key, either in a subkey named Parameters, if this exists, else in the key itself. (This fall-back is not supported until version 6.0. Earlier versions of SVCHOST recognise these settings in the Parameters subkey only.)
Note that although each service corresponds to one service DLL, the reverse is not required, though it does look to be the intended (or at least favoured) design. After all, if you code multiple services in the one service DLL, then you perhaps do not gain very much over coding them in the one service program. That said, the framework that SVCHOST provides for service DLLs is not insubstantial, especially for COM security, and examples do exist of the one service DLL implementing multiple services. See for instance SHSVCS.DLL, which handles both ShellHWDetection and Themes, and IISW3ADM.DLL, which handles both W3SVC and WAS. (Now, some may wonder if it’s entirely fair that IIS knows anything at all about running under SVCHOST. After all, IIS is just one of potentially many competing web servers that one might want to run on the monopoly product that is Windows. Is it unlawfully favoured, knowing a Windows technology that the Windows SDK doesn’t document?)
An essential detail is where to find the service DLL for a given service:
The ServiceDll setting is required. There is no default, such as assuming that the DLL has the same name as its service, with “.dll” appended. Neither is there any allowance for an implied location. Indeed, this configuration is quite fussy: the pathname must be given as string data of the type that allows for expansion of environment variables. When the string data expands to a pathname, as would be typical, the loading of associated executables is subject to what the LoadLibraryEx documentation describes as the alternate search order.
A relatively recent development is to provide that the service DLL be loaded in an activation context created from a manifest:
|Availability||version 5.2 and higher|
Though this value is optional, its presence with the wrong data type or with an empty string as data is an error, such that the service DLL will not be loaded. If ServiceManifest is present and valid, the path component from ServiceDll is ignored, so that the DLL is instead loaded from wherever the manifest redirects.
Just as each service implemented in a service program has a ServiceMain function, so does each service DLL. An important difference is that service DLLs do not specify this function by calling StartServiceCtrlDispatcher from their initialisation code. Instead, they export ServiceMain so that SVCHOST can find it. The name is replaceable, but from the registry so that SVCHOST can know it:
Except for being exported (and for the note below about registering a stop callback), ServiceMain in a service DLL should be coded to follow Microsoft’s documented requirements for this same function in a service program. In particular, it calls RegisterServiceCtrlHandlerEx to set up the handling of control requests for the service. SVCHOST is not involved with control requests for a started service.
In version 5.1 and higher, SVCHOST provides that each service DLL may export another function, necessarily named SvchostPushServiceGlobals, for SVCHOST to call before ServiceMain to tell the service DLL of shared data and common functions.
Recent SVCHOST versions provide that a service DLL may re-involve SVCHOST when a service is stopped. Where the ServiceMain function would otherwise call RegisterWaitForSingleObject, following Microsoft’s documentation, it should instead call SVCHOST’s RegisterStopCallback function, whose address will have been learnt when SVCHOST called the DLL’s exported SvchostPushServiceGlobals function. The callback as registered with SVCHOST can be exactly as registered through RegisterWaitForSingleObject. On receiving a request to stop the service, the handling can be exactly as before, as far as concerns the service DLL. The difference is that the callback returns to SVCHOST, which can then unload the DLL. To have SVCHOST unload a service DLL whose services are all stopped, the following registry value must be set to 1:
|Availability||version 5.1 from Windows XP SP2, version 5.2 from Windows Server 2003 SP1, and higher|
The ServiceDllUnloadOnStop setting also supports an old technique which Microsoft’s documentation of ServiceMain notes as having been the “common practice” before Windows 2000 introduced the RegisterWaitForSingleObject function. After setting the service’s status as SERVICE_RUNNING, the ServiceMain function can wait on an event instead of returning. Controls are received in a different thread. When the service is to stop, it signals the event that ServiceMain is waiting on. When ServiceMain returns, SVCHOST can unload the DLL. Though this technique is inferior, if only for making the system maintain a thread unnecessarily, it is relied on by many of Microsoft’s service DLLs even as late as Windows Vista. Examples are as varied as BROWSER.DLL, CSCSVC.DLL, IISW3ADM.DLL and SHSVCS.DLL.
Note that if ServiceDllUnloadOnStop is 1, then the service DLL is assumed to use one or other of these techniques for each of its services. If it returns from ServiceMain without having registered a stop callback, then it is liable to get unloaded.
Note also that the setting is live. SVCHOST keeps a reference count for each service. This count is incremented before calling ServiceMain and when registering a stop callback. It is decremented when ServiceMain returns and after the stop callback has executed. At each decrement (and, curiously, not just the final decrement), SVCHOST reads the registry value afresh.
The ServiceDllUnloadOnStop setting is unusual for being mentioned in the Windows SDK. See Guidelines for Services (Windows) in a section about the Restart Manager. This must be an oversight. Microsoft writes much of (if not all) its documentation for Microsoft’s own programmers first and foremost, and then selects what to publish to the rest of the world. Things sometimes slip out. Here, for instance, we have “Services that run in the context of svchost.exe should register to unload DLLs when the service is shut down”, and the documentation that follows is even accurate, but nowhere in the Windows SDK is there any information on how anyone outside Microsoft might write such a service, let alone know what is meant by SvchostPushServiceGlobals or RegisterStopCallback.