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