Writing a ServiceMain Function Example
The SvcMain() function in the following example is the ServiceMain function for the example service. SvcMain() has access to the command-line arguments for the service in the way that the main function of a console application does. The first parameter contains the number of arguments being passed to the service in the second parameter. There will always be at least one argument. The second parameter is a pointer to an array of string pointers. The first item in the array is always the service name. The SvcMain() function first calls the RegisterServiceCtrlHandler() function to register the SvcCtrlHandler() function as the service's Handler function and begin initialization. RegisterServiceCtrlHandler() should be the first nonfailing function in ServiceMain so the service can use the status handle returned by this function to call SetServiceStatus() with the SERVICE_STOPPED state if an error occurs.
Next, the SvcMain() function calls the ReportSvcStatus() function to indicate that its initial status is SERVICE_START_PENDING. While the service is in this state, no controls are accepted. To simplify the logic of the service, it is recommended that the service not accept any controls while it is performing its initialization. Finally, the SvcMain() function calls the SvcInit() function to perform the service-specific initialization and begin the work to be performed by the service. The sample initialization function, SvcInit(), is a very simple example; it does not perform more complex initialization tasks such as creating additional threads. It creates an event that the service control handler can signal to indicate that the service should stop, then calls ReportSvcStatus() to indicate that the service has entered the SERVICE_RUNNING state. At this point, the service has completed its initialization and is ready to accept controls. For best system performance, your application should enter the running state within 25-100 milliseconds.
Because this sample service does not complete any real tasks, SvcInit() simply waits for the service stop event to be signaled by calling the WaitForSingleObject() function, calls ReportSvcStatus() to indicate that the service has entered the SERVICE_STOPPED state, and returns. (Note that it is important for the function to return, rather than call the ExitThread() function, because returning allows for cleanup of the memory allocated for the arguments.) You can perform additional cleanup tasks by using the RegisterWaitForSingleObject() function instead of WaitForSingleObject(). The thread that is running the ServiceMain function terminates, but the service itself continues to run. When the service control handler signals the event, a thread from the thread pool executes your callback to perform the additional cleanup, including setting the status to SERVICE_STOPPED. Note that this example uses SvcReportEvent() to write error events to the event log. The following global definitions are used in this sample.
#define SVCNAME LSvcName
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
The following sample fragment is taken from the complete service sample.
#include <windows.h>
#include <stdio.h>
#define SVCNAME LSvcName
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
// Prototypes
void SvcInit(DWORD dwArgc, LPTSTR *lpszArgv);
void ReportSvcStatus(DWORD, DWORD, DWORD);
void SvcReportEvent(LPTSTR szFunction);
void WINAPI SvcCtrlHandler(DWORD dwCtrl);
// Purpose: Entry point for the process
// Parameters: None
// Return value: None
int wmain(int argc, WCHAR *argv[])
{
// Code goes here
return 0;
}
// Purpose: Entry point for the service
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of
// the service and subsequent strings are passed by the process
// that called the StartService function to start the service.//
// Return value: None.
void WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler);
if(!gSvcStatusHandle)
{
SvcReportEvent(LRegisterServiceCtrlHandler);
return;
}
// These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gSvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000);
// Perform service-specific initialization and work.
SvcInit(dwArgc, lpszArgv);
}
// Purpose: The service code
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of
// the service and subsequent strings are passed by the process
// that called the StartService function to start the service.//
// Return value: None
void SvcInit(DWORD dwArgc, LPTSTR *lpszArgv)
{
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with
// SERVICE_START_PENDING. If initialization fails, call
// ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler(),
// signals this event when it receives the stop control code.
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if (ghSvcStopEvent == NULL)
{
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
// Report running status when initialization is complete
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
// TO_DO: Perform work until service stops.
while(1)
{
// Check whether to stop the service
WaitForSingleObject(ghSvcStopEvent, INFINITE);
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
}
// Purpose: Sets the current service status and reports it to the SCM.
// Parameters:
// dwCurrentState - The current state (see SERVICE_STATUS)
// dwWin32ExitCode - The system error code
// dwWaitHint - Estimated time for pending operation, in milliseconds//
// Return value: None
void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
gSvcStatus.dwControlsAccepted = 0;
else
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
gSvcStatus.dwCheckPoint = 0;
else
gSvcStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the SCM.
SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}
void SvcReportEvent(LPTSTR szFunction)
{
// Code goes here
}
// Purpose: Called by SCM whenever a control code is sent to the service using the ControlService function.
// Parameters: dwCtrl - control code
// Return value: None
void WINAPI SvcCtrlHandler(DWORD dwCtrl)
{
// Handle the requested control code.
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop
SetEvent(ghSvcStopEvent);
return;
case SERVICE_CONTROL_INTERROGATE:
// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}