Environment: Visual C++6 SP5, Windows 2000/XP
Introduction
A few weeks ago, I was trying to make my own server application into a service without much knowledge about how to do it. Now, I know more about services and would like to share my knowledge in this article.
Services are processes that can be auto started when the system boots, and can be left them running while the system is on. For example, system services are services such as an Event log that logs messages issued by Windows-based programs and components to be viewed in Event Viewer. Services are similar to UNIX’s daemons.
A Windows service applet can be found in Control Panel > Administrator Tools > Services. Any new services installation creates an entry in this applet and in the Registry (HKEY_LOCAL_MACHINESystemCurrentControlSetServices), so that the system knows about it each time it boots.
Every service must be able to respond to a set of standard events that the services applet can pass to it. These events are shown in the window service as buttons:
- START: Starts the service if it is a manual start service or it is stopped.
- STOP: Stops the service.
- PAUSE: Suspends the service temporarily.
- CONTINUE: Resumes the suspended service.
All services are managed under a system called the Service Control Manager. It maintains the list of known services in the Registry and starts them at boot time or when requested.
A program that acts as a service is a normal EXE file, but it must meet special requirements so that it interfaces properly with the Service Control Manager (SCM). The requirements follow:
- EXE must have a normal main or WinMain function. Call the StartServiceCrtlDispatcher function to register the EXE with the SCM and give the SCM a pointer to the ServiceMain function.
- SCM will call the ServiceMain function when it wants to start the service. The ServiceMain should immediately call the RegisterServiceCtrlHandler function to register a Handler function with SCM.
- The Handler function contains switch statements that parse control requests received from the SCM.
- When you create an EXE that contains that main, ServiceMain, and Handler functions, as well as a function that contains the thread for the service itself, you will have a complete service program.
void main(int argc, char *argv[])
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL}
};
BOOL success;
if(argc == 2)
//..check arguments
else
{
//register with SCM
success = StartServiceCtrlDispatcher(serviceTable);
if (!success)
ErrorHandler("StartServiceCtrlDispatcher",
GetLastError());
}
}
void ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
//immediately call registration function
serviceStatusHandle =
RegisterServiceCtrlHandler(ServiceName,
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
//notify SCM
//create termination event
terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
.
.
//notify SCM
.
.
//check for startup parameter
//start service
//notify SCM service is runnning
SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
//wait for stop signal and then terminate
WaitForSingleObject(terminateEvent, INFINITE);
terminate(0);
}
void ServiceCtrlHandler(DWORD controlCode)
{
DWORD currentState = 0;
BOOL success;
switch(controlCode)
{
// START = ServiceMain()
// STOP
case SERVICE_CONTROL_STOP:
currentState = SERVICE_STOP_PENDING;
//notify SCM
SendStatusToSCM(SERVICE_STOP_PENDING,
NO_ERROR,
0,
1,
5000);
//stop service
StopService();
return;
// PAUSE
case SERVICE_CONTROL_PAUSE:
if (runningService && !pauseService)
{
//notify SCM
success = SendStatusToSCM(
SERVICE_PAUSE_PENDING,
NO_ERROR,
0,
1,
1000);
PauseService();
currentState = SERVICE_PAUSED;
}
break;
// RESUME
case SERVICE_CONTROL_CONTINUE:
if (runningService && pauseService)
{
//notify SCM
success = SendStatusToSCM(
SERVICE_CONTINUE_PENDING,
NO_ERROR,
0,
1,
1000);
ResumeService();
currentState = SERVICE_RUNNING;
}
break;
// UPDATE
case SERVICE_CONTROL_INTERROGATE:
//update status out of switch()
break;
case SERVICE_CONTROL_SHUTDOWN:
//do nothing
return;
default:
break;
}
//notify SCM current state
SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
Template for the Service Program
The accompanying .cpp file is a template for the service program, service control program (RUN, STOP, PAUSE, CONTINUE), and also the configuration program in the sense that it can install or remove the EXE itself using command.
Usage
- Add service.cpp to the project workspace.
- Change the ServiceName under the global variable to the desired name.
- Add your function into ServiceThread().
- DONE!
To Install
- Use command prompt.
- Locate the EXE.
- Type <programname> -i to install
Type <programname> -u to uninstall
Type <programname> -r to run
Type <programname> -s to stop
Type <programname> -p to pause
Type <programname> -c to continue
Type <programname> help