Scrittura di una funzione ServiceMain

La funzione SvcMain nell'esempio seguente è la funzione ServiceMain per il servizio di esempio. SvcMain ha accesso agli argomenti della riga di comando per il servizio nel modo in cui la funzione principale di un'applicazione console esegue. Il primo parametro contiene il numero di argomenti passati al servizio nel secondo parametro. Ci sarà sempre almeno un argomento. Il secondo parametro è un puntatore a una matrice di puntatori di stringa. Il primo elemento della matrice è sempre il nome del servizio.

La funzione SvcMain chiama prima la funzione RegisterServiceCtrlHandler per registrare la funzione SvcCtrlHandler come funzione gestore del servizio e avviare l'inizializzazione. RegisterServiceCtrlHandler deve essere la prima funzione nonfailing in ServiceMain in modo che il servizio possa usare l'handle di stato restituito da questa funzione per chiamare SetServiceStatus con lo stato SERVICE_STOPPED se si verifica un errore.

Successivamente, la funzione SvcMain chiama la funzione ReportSvcStatus per indicare che lo stato iniziale è SERVICE_START_PENDING. Mentre il servizio si trova in questo stato, non vengono accettati controlli. Per semplificare la logica del servizio, è consigliabile che il servizio non accetti alcun controllo mentre esegue l'inizializzazione.

Infine, la funzione SvcMain chiama la funzione SvcInit per eseguire l'inizializzazione specifica del servizio e avviare il lavoro da eseguire dal servizio.

La funzione di inizializzazione di esempio, SvcInit, è un esempio molto semplice; non esegue attività di inizializzazione più complesse, ad esempio la creazione di thread aggiuntivi. Crea un evento che il gestore del controllo del servizio può segnalare per indicare che il servizio deve arrestare, quindi chiama ReportSvcStatus per indicare che il servizio ha immesso lo stato di SERVICE_RUNNING. A questo punto, il servizio ha completato l'inizializzazione e è pronto per accettare i controlli. Per migliorare le prestazioni del sistema, l'applicazione deve immettere lo stato in esecuzione entro 25-100 millisecondi.

Poiché questo servizio di esempio non completa attività reali, SvcInit attende semplicemente che l'evento di arresto del servizio venga segnalato chiamando la funzione WaitForSingleObject , chiama ReportSvcStatus per indicare che il servizio ha immesso lo stato SERVICE_STOPPED e restituisce. Si noti che è importante che la funzione restituisca, anziché chiamare la funzione ExitThread , perché la restituzione consente la pulizia della memoria allocata per gli argomenti. È possibile eseguire attività di pulizia aggiuntive usando la funzione RegisterWaitForSingleObject anziché WaitForSingleObject. Il thread che esegue la funzione ServiceMain termina, ma il servizio stesso continua a essere eseguito. Quando il gestore del controllo del servizio segnala l'evento, un thread dal pool di thread esegue il callback per eseguire la pulizia aggiuntiva, inclusa l'impostazione dello stato su SERVICE_STOPPED.

Si noti che questo esempio usa SvcReportEvent per scrivere eventi di errore nel registro eventi. Per il codice sorgente per SvcReportEvent, vedere Svc.cpp. Per una funzione del gestore del controllo di esempio, vedere Scrittura di una funzione gestore di controllo.

In questo esempio vengono usate le definizioni globali seguenti.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

Il frammento di esempio seguente viene tratto dall'esempio di servizio 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 );
}

Funzione ServiceMain

Esempio completo del servizio