Schreiben einer ServiceMain-Funktion

Die SvcMain-Funktion im folgenden Beispiel ist die ServiceMain-Funktion für den Beispieldienst. SvcMain hat Zugriff auf die Befehlszeilenargumente für den Dienst wie die Standard-Funktion einer Konsolenanwendung. Der erste Parameter enthält die Anzahl der Argumente, die im zweiten Parameter an den Dienst übergeben werden. Es gibt immer mindestens ein Argument. Der zweite Parameter ist ein Zeiger auf ein Array von Zeichenfolgenzeigern. Das erste Element im Array ist immer der Dienstname.

Die SvcMain-Funktion ruft zuerst die RegisterServiceCtrlHandler-Funktion auf, um die SvcCtrlHandler-Funktion als Handlerfunktion des Diensts zu registrieren und mit der Initialisierung zu beginnen. RegisterServiceCtrlHandler sollte die erste nicht enthaltende Funktion in ServiceMain sein, damit der Dienst das von dieser Funktion zurückgegebene status-Handle verwenden kann, um SetServiceStatus mit dem SERVICE_STOPPED Zustand aufzurufen, wenn ein Fehler auftritt.

Als Nächstes ruft die SvcMain-Funktion die ReportSvcStatus-Funktion auf, um anzugeben, dass die anfängliche status SERVICE_START_PENDING ist. Während sich der Dienst in diesem Zustand befindet, werden keine Steuerelemente akzeptiert. Um die Logik des Diensts zu vereinfachen, wird empfohlen, dass der Dienst während der Initialisierung keine Steuerelemente akzeptiert.

Schließlich ruft die SvcMain-Funktion die SvcInit-Funktion auf, um die dienstspezifische Initialisierung auszuführen und mit der Arbeit zu beginnen, die vom Dienst ausgeführt werden soll.

Die Beispielinitialisierungsfunktion SvcInit ist ein sehr einfaches Beispiel. Komplexere Initialisierungsaufgaben wie das Erstellen zusätzlicher Threads werden nicht ausgeführt. Es erstellt ein Ereignis, das der Dienststeuerungshandler signalisieren kann, um anzugeben, dass der Dienst beendet werden soll, und ruft dann ReportSvcStatus auf, um anzugeben, dass der Dienst in den SERVICE_RUNNING Zustand eingetreten ist. An diesem Punkt hat der Dienst seine Initialisierung abgeschlossen und ist bereit, Steuerelemente zu akzeptieren. Um eine optimale Systemleistung zu erzielen, sollte Ihre Anwendung innerhalb von 25 bis 100 Millisekunden in den Ausführungszustand wechseln.

Da dieser Beispieldienst keine echten Aufgaben ausführt, wartet SvcInit einfach, bis das Dienststoppereignis durch Aufrufen der WaitForSingleObject-Funktion signalisiert wird, ruft ReportSvcStatus auf, um anzugeben, dass der Dienst den SERVICE_STOPPED Zustand eingegeben hat, und gibt zurück. (Beachten Sie, dass es wichtig ist, dass die Funktion zurückgegeben wird, anstatt die ExitThread-Funktion aufzurufen, da die Rückgabe die Bereinigung des für die Argumente zugeordneten Arbeitsspeichers ermöglicht.) Sie können zusätzliche Bereinigungsaufgaben ausführen, indem Sie die RegisterWaitForSingleObject-Funktion anstelle von WaitForSingleObject verwenden. Der Thread, der die ServiceMain-Funktion ausführt, wird beendet, aber der Dienst selbst wird weiterhin ausgeführt. Wenn der Dienststeuerungshandler das Ereignis signalisiert, führt ein Thread aus dem Threadpool ihren Rückruf aus, um die zusätzliche Bereinigung durchzuführen, einschließlich der Festlegung der status auf SERVICE_STOPPED.

Beachten Sie, dass in diesem Beispiel SvcReportEvent verwendet wird, um Fehlerereignisse in das Ereignisprotokoll zu schreiben. Den Quellcode für SvcReportEvent finden Sie unter Svc.cpp. Eine Beispielfunktion für einen Steuerelementhandler finden Sie unter Schreiben einer Steuerelementhandlerfunktion.

In diesem Beispiel werden die folgenden globalen Definitionen verwendet.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

Das folgende Beispielfragment stammt aus dem vollständigen Dienstbeispiel.

//
// 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-Funktion

Beispiel für den vollständigen Dienst