What is a Windows 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/2003/Vista/2008. 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
Source code is in here.
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.
References
When I was writing this page I used search engine to find
articles about services and to my
surprise I found one sample by
CommSoft which is called "BeeperService".