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 in der Weise, wie die Hauptfunktion einer Konsolenanwendung ausgeführt wird. Der erste Parameter enthält die Anzahl der Argumente, die an den Dienst im zweiten Parameter ü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 Funktion des Diensts zu registrieren und die Initialisierung zu beginnen. RegisterServiceCtrlHandler sollte die erste nichtfailierende Funktion in ServiceMain sein, sodass der Dienst den status handle verwenden kann, der von dieser Funktion zurückgegeben wird, um SetServiceStatus mit dem SERVICE_STOPPED Zustand aufzurufen, wenn ein Fehler auftritt.

Als Nächstes ruft die Funktion "SvcMain" die Funktion "ReportSvcStatus" auf, um anzugeben, dass der 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 die Arbeit zu beginnen, die vom Dienst ausgeführt werden soll.

Die Beispiel-Initialisierungsfunktion SvcInit ist ein sehr einfaches Beispiel; es führt keine komplexeren Initialisierungsaufgaben aus, z. B. das Erstellen zusätzlicher Threads. 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 den SERVICE_RUNNING Zustand eingegeben hat. An diesem Punkt hat der Dienst seine Initialisierung abgeschlossen und ist bereit, Steuerelemente zu akzeptieren. Für die beste Systemleistung sollte Ihre Anwendung den Ausführungszustand innerhalb von 25-100 Millisekunden eingeben.

Da dieser Beispieldienst keine echten Vorgänge abgeschlossen hat, wartet SvcInit einfach darauf, dass das Dienststoppereignis durch Aufrufen der WaitForSingleObject-Funktion signalisiert wird, ruft ReportSvcStatus auf, um anzugeben, dass der Dienst den SERVICE_STOPPED Zustand eingegeben und zurückgegeben hat. (Beachten Sie, dass es wichtig ist, dass die Funktion zurückgegeben werden soll, anstatt die ExitThread-Funktion aufzurufen, da die Rückgabe die Bereinigung des Speichers für die Argumente ermöglicht.) Sie können zusätzliche Bereinigungsaufgaben ausführen, indem Sie die Funktion "RegisterWaitForSingleObject " anstelle von WaitForSingleObject verwenden. Der Thread, der die ServiceMain-Funktion ausführt, 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 auszuführen, einschließlich der Einstellung des Status auf SERVICE_STOPPED.

Beachten Sie, dass in diesem Beispiel SvcReportEvent zum Schreiben von Fehlerereignissen in das Ereignisprotokoll verwendet wird. Informationen zum Quellcode für SvcReportEvent finden Sie unter Svc.cpp. Eine Beispielfunktion für Steuerelementhandler finden Sie unter Schreiben einer Steuerelementhandlerfunktion.

Die folgenden globalen Definitionen werden in diesem Beispiel verwendet.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

Das folgende Beispielfragment wird aus dem vollständigen Dienstbeispiel entnommen.

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

Beispiel für den vollständigen Dienst