Windows Services Programming 11

 

 

 

 

 

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 L"SvcName"

 

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 L"SvcName"

 

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(L"RegisterServiceCtrlHandler");

        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);

}

 

 

 

 

< Windows Services 10 | Win32 Programming | Windows Services Win32 Programming | Windows Services 12 >