Partager via


Écriture d’une fonction ServiceMain

La fonction SvcMain dans l’exemple suivant est la fonction ServiceMain pour l’exemple de service. SvcMain a accès aux arguments de ligne de commande pour le service comme le fait la fonction main d’une application console. Le premier paramètre contient le nombre d’arguments passés au service dans le deuxième paramètre. Il y aura toujours au moins un argument. Le deuxième paramètre est un pointeur vers un tableau de pointeurs de chaîne. Le premier élément du tableau est toujours le nom du service.

La fonction SvcMain appelle d’abord la fonction RegisterServiceCtrlHandler pour inscrire la fonction SvcCtrlHandler en tant que fonction Handler du service et commencer l’initialisation. RegisterServiceCtrlHandler doit être la première fonction non dynamique dans ServiceMain afin que le service puisse utiliser le handle status retourné par cette fonction pour appeler SetServiceStatus avec l’état SERVICE_STOPPED si une erreur se produit.

Ensuite, la fonction SvcMain appelle la fonction ReportSvcStatus pour indiquer que son status initial est SERVICE_START_PENDING. Tant que le service est dans cet état, aucun contrôle n’est accepté. Pour simplifier la logique du service, il est recommandé que le service n’accepte aucun contrôle pendant l’initialisation.

Enfin, la fonction SvcMain appelle la fonction SvcInit pour effectuer l’initialisation spécifique au service et commencer le travail à effectuer par le service.

L’exemple de fonction d’initialisation, SvcInit, est un exemple très simple ; il n’effectue pas de tâches d’initialisation plus complexes telles que la création de threads supplémentaires. Il crée un événement que le gestionnaire de contrôle de service peut signaler pour indiquer que le service doit s’arrêter, puis appelle ReportSvcStatus pour indiquer que le service est entré dans l’état SERVICE_RUNNING. À ce stade, le service a terminé son initialisation et est prêt à accepter les contrôles. Pour optimiser les performances du système, votre application doit entrer l’état d’exécution dans un délai de 25 à 100 millisecondes.

Étant donné que cet exemple de service n’effectue pas de tâches réelles, SvcInit attend simplement que l’événement d’arrêt du service soit signalé en appelant la fonction WaitForSingleObject , appelle ReportSvcStatus pour indiquer que le service est entré dans l’état SERVICE_STOPPED et retourne. (Notez qu’il est important que la fonction retourne, plutôt que d’appeler la fonction ExitThread , car le retour permet de nettoyer la mémoire allouée pour les arguments.) Vous pouvez effectuer des tâches de nettoyage supplémentaires à l’aide de la fonction RegisterWaitForSingleObject au lieu de WaitForSingleObject. Le thread qui exécute la fonction ServiceMain s’arrête, mais le service lui-même continue de s’exécuter. Lorsque le gestionnaire de contrôle de service signale l’événement, un thread du pool de threads exécute votre rappel pour effectuer le nettoyage supplémentaire, notamment en définissant le status sur SERVICE_STOPPED.

Notez que cet exemple utilise SvcReportEvent pour écrire des événements d’erreur dans le journal des événements. Pour obtenir le code source de SvcReportEvent, consultez Svc.cpp. Pour obtenir un exemple de fonction de gestionnaire de contrôles, consultez Écriture d’une fonction de gestionnaire de contrôle.

Les définitions globales suivantes sont utilisées dans cet exemple.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

L’exemple de fragment suivant est extrait de l’exemple de service complet.

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

ServiceMain, fonction

Exemple de service complet