Escritura de una función ServiceMain

La función SvcMain del ejemplo siguiente es la función ServiceMain del servicio de ejemplo. SvcMain tiene acceso a los argumentos de la línea de comandos para el servicio de la manera en que lo hace la función principal de una aplicación de consola. El primer parámetro contiene el número de argumentos que se pasan al servicio en el segundo parámetro. Siempre habrá al menos un argumento. El segundo parámetro es un puntero a una matriz de punteros de cadena. El primer elemento de la matriz siempre es el nombre del servicio.

La función SvcMain llama primero a la función RegisterServiceCtrlHandler para registrar la función SvcCtrlHandler como función handler del servicio y comenzar la inicialización. RegisterServiceCtrlHandler debe ser la primera función que no es de confianza en ServiceMain para que el servicio pueda usar el identificador de estado devuelto por esta función para llamar a SetServiceStatus con el estado SERVICE_STOPPED si se produce un error.

A continuación, la función SvcMain llama a la función ReportSvcStatus para indicar que su estado inicial es SERVICE_START_PENDING. Mientras el servicio está en este estado, no se aceptan controles. Para simplificar la lógica del servicio, se recomienda que el servicio no acepte ningún control mientras realiza su inicialización.

Por último, la función SvcMain llama a la función SvcInit para realizar la inicialización específica del servicio y comenzar el trabajo que va a realizar el servicio.

La función de inicialización de ejemplo, SvcInit, es un ejemplo muy sencillo; no realiza tareas de inicialización más complejas, como crear subprocesos adicionales. Crea un evento que el controlador de control de servicio puede indicar para indicar que el servicio debe detenerse y, a continuación, llama a ReportSvcStatus para indicar que el servicio ha entrado en el estado SERVICE_RUNNING. En este momento, el servicio ha completado su inicialización y está listo para aceptar controles. Para obtener el mejor rendimiento del sistema, la aplicación debe especificar el estado en ejecución en 25-100 milisegundos.

Dado que este servicio de ejemplo no completa ninguna tarea real, SvcInit simplemente espera a que se señale el evento de detención del servicio llamando a la función WaitForSingleObject , llama a ReportSvcStatus para indicar que el servicio ha entrado en el estado de SERVICE_STOPPED y devuelve. (Tenga en cuenta que es importante que la función devuelva, en lugar de llamar a la función ExitThread , porque devolver permite limpiar la memoria asignada para los argumentos). Puede realizar tareas de limpieza adicionales mediante la función RegisterWaitForSingleObject en lugar de WaitForSingleObject. El subproceso que ejecuta la función ServiceMain finaliza, pero el propio servicio continúa ejecutándose. Cuando el controlador de control de servicio señala el evento, un subproceso del grupo de subprocesos ejecuta la devolución de llamada para realizar la limpieza adicional, incluida la configuración del estado en SERVICE_STOPPED.

Tenga en cuenta que en este ejemplo se usa SvcReportEvent para escribir eventos de error en el registro de eventos. Para obtener el código fuente de SvcReportEvent, consulte Svc.cpp. Para obtener un ejemplo de función de controlador de control, consulte Escritura de una función de controlador de control.

En este ejemplo se usan las siguientes definiciones globales.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

El siguiente fragmento de ejemplo se toma del ejemplo de servicio completo.

//
// 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(TEXT("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 );
}

Service ServiceMain (función)

Ejemplo de servicio completo