What is a service?
A service is a process which does not require user to log on. Service starts when system boots up, it is started from Services applet or when other application starts it.
Service can have command line or graphical user interface, but I don't recommend to use it. If you need a user interface to the service, write a separate application and choose which communication technique to use. I suggest sockets because it allows you to operate service from anywhere in the internet. If you choose e.g. file mapping, you are restricted to run GUI in the same computer where the service is running. If you choose e.g. named pipes, you are restricted to run GUI in some machine in local area network (LAN).
By the way, you can write service and GUI into a single application. You just need a way to decide which code to execute when process is started. Process can make the decision itself by checking SIDs of the process.
Services are supported by Windows NT/2000/XP. Windows 95 series does not support services.
Security
Usually a service is executed in the account of Local System. Service can execute code which requires e.g. SE_TCB_NAME privilege like LogonUser. Service cannot use operating system features that require user to logon such as HKEY_CURRENT_USER registry key and some local area network functions.
Service can be allowed to interact with desktop when Local System account is chosen.
Service can be executed in the specified user's account.
How to write a service?
A service process can contain one or multiple services. Service is usually a console application.
Sample Service
I have written a very short service called "Beeper Service". Its only task is to beep every 5 seconds. Because it does only this, this sample explains only "how to write a service". Nothing else. Next I'll describe its parts.
This example is in C which is compiled with C++ compiler.
Running a service
First, you setup a table of services (SERVICE_TABLE_ENTRY) and call StartServiceCtrlDispatcher which does not return until all services in the process have terminated. SERVICE_TABLE_ENTRY requires a name of the service and a function for the service. Every service requires a separate function. You can give any valid name for the function. For SERVICE_WIN32_OWN_PROCESS it's usually ServiceMain. Here's the code:
קוד:
void RunService()
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ serviceName, ServiceMain },
{ 0, 0 }
};
StartServiceCtrlDispatcher( serviceTable );
}
ServiceMain
Service control manager requests a main thread (that is the thread which is executing StartServiceCtrlDispatcher) to create a thread for the service. So, in service process there are always at least two threads running. A main thread and one thread for each running service. Of course, a service may create more threads when needed.
Service needs SERVICE_STATUS structure. It is needed when service tells its status to service control manager. I think that the best place to initialise SERVICE_STATUS is in the beginning of ServiceMain.
After that ServiceMain should call the RegisterServiceCtrlHandler function to specify a ServiceControlHandler function to handle control requests. Each service require a separate control handler function. You can give any valid name for the function.
Next, ServiceMain have to call SetServiceStatus function to send status information to the service control manager. Usually service tells that it's starting with SERVICE_START_PENDING. Then it initialises every needed things. When this is done, it accepts control requests SERVICE_ACCEPT_STOP and SERVICE_ACCEPT_SHUTDOWN and sets its current state to SERVICE_RUNNING and tells all this to service control manager with call to SetServiceStatus.
Now service control manager knows that service is up and running. Service should be running until stopped or paused.
How to know when to stop the service?
For this you need a way to control stopping. Service control manager calls ServiceControlHandler with control code SERVICE_CONTROL_STOP when it is requested to stop service.
I have always done it with event with name stopServiceEvent. I usually wait events with WaitForMultipleObjects. In this example I use WaitForSingleObject. When stopServiceEvent is signaled, the service is stopped.
Now, the service is stopping, so tell current status SERVICE_STOP_PENDING to service control manager. Do cleanup and finally stop accepting control requests SERVICE_ACCEPT_STOP and SERVICE_ACCEPT_SHUTDOWN and set service's current state to SERVICE_STOPPED and tell this to service control manager with call to SetServiceStatus. Service is now terminated.
Here is the code:
קוד:
void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
// initialise service status
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );
if ( serviceStatusHandle )
{
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do initialisation here
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
// running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
do
{
Beep( 1000, 100 );
}
while ( WaitForSingleObject( stopServiceEvent, 5000 ) == WAIT_TIMEOUT );
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do cleanup here
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
ServiceControlHandler
Service control manager calls this function in the context of a main thread of service process when it or some other wants to control service.
If control code is SERVICE_CONTROL_STOP, service is requested to stop. First, report status SERVICE_STOP_PENDING to service control manager with SetServiceStatus. Then, make service to stop. It's easiest with SetEvent.
For SERVICE_CONTROL_SHUTDOWN you can do the same things as for SERVICE_CONTROL_STOP.
For every other control codes it's wise to report service's current status. Here's the code:
קוד:
void WINAPI ServiceControlHandler( DWORD controlCode )
{
switch ( controlCode )
{
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
SetEvent( stopServiceEvent );
return;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
default:
if ( controlCode >= 128 && controlCode <= 255 )
// user defined control code
break;
else
// unrecognised control code
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
Debugging a service
Debugging a service is easy, if you know how to do it.
Running service program from development tool or from console does not work. StartServiceCtrlDispatcher fails because service control manager is not waiting for calls. If you start service as a real service, you cannot debug it.
My way to debug a service consists of following things:
examine the execution context of the process
set console control handler
create and run service in separate thread (simulate service)
By examining the execution context, process can determine is it running as a service. If not, console mode is used and thread is created for service. In console mode Ctrl-C and other handlers are set and the code that stops a service is the same as in the real service.
You cannot debug everything in this way because security context is not same as when process is executed as a service. For example LogonUser will fail in console mode while it works in service mode.
I haven't written a sample of this yet. I have it in my C++ class library. I'll try to find the time to add it to this sample.
Installing a service
Before you can start service, you have to install it. First, open service control manager on target computer with OpenSCManager. Then create service with CreateService. You have to decide a service name and a display name you use. Service name is the name you use to reference to your service.
Uninstalling a service
See the source code.
Source code
What's missing?
I know more things about services than I wrote in here. If you didn't understand something or want to learn more, please ask by sending e-mail to
jarmo@muukka.net. I am planning to update this page.
References