Udostępnij przez


Pisanie funkcji ServiceMain

Funkcja SvcMain w poniższym przykładzie to funkcja ServiceMain dla przykładowej usługi. SvcMain ma dostęp do argumentów wiersza polecenia dla usługi tak jak funkcja główna aplikacji konsolowej. Pierwszy parametr zawiera liczbę argumentów przekazywanych do usługi w drugim parametrze. Zawsze będzie co najmniej jeden argument. Drugi parametr jest wskaźnikiem do tablicy wskaźników ciągów znaków. Pierwszy element w tablicy jest zawsze nazwą usługi.

Funkcja SvcMain najpierw wywołuje funkcjęRegisterServiceCtrlHandler, aby zarejestrować funkcję SvcCtrlHandler jako funkcję programu obsługi usługi i rozpocząć inicjowanie. RegisterServiceCtrlHandler powinna być pierwszą niezawodną funkcją w ServiceMain, aby usługa mogła użyć uchwytu statusu zwróconego przez tę funkcję do wywołania SetServiceStatus ze stanem SERVICE_STOPPED, jeśli wystąpi błąd.

Następnie funkcja SvcMain wywołuje funkcję ReportSvcStatus, aby wskazać, że jej początkowy stan to SERVICE_START_PENDING. Gdy usługa jest w tym stanie, żadne kontrolki nie są akceptowane. Aby uprościć logikę usługi, zaleca się, aby usługa nie akceptowała żadnych kontrolek podczas inicjowania.

Na koniec funkcja SvcMain wywołuje funkcję SvcInit w celu wykonania inicjowania specyficznego dla usługi i rozpoczęcia pracy, która ma być wykonywana przez usługę.

Przykładowa funkcja inicjowania, SvcInit, jest bardzo prostym przykładem; nie wykonuje bardziej złożonych zadań inicjowania, takich jak tworzenie dodatkowych wątków. Tworzy zdarzenie, które program obsługi kontroli usługi może sygnalizować, aby wskazać, że usługa powinna zostać zatrzymana, a następnie wywołuje funkcję ReportSvcStatus, aby wskazać, że usługa weszła w stan SERVICE_RUNNING. W tym momencie usługa zakończyła inicjowanie i jest gotowa do akceptowania kontrolek. Aby uzyskać najlepszą wydajność systemu, aplikacja powinna wprowadzić stan działania w ciągu 25–100 milisekund.

Ponieważ ta przykładowa usługa nie wykonuje żadnych rzeczywistych zadań, narzędzie SvcInit po prostu czeka na zasygnalizowanie zdarzenia zatrzymania usługi przez wywołanie funkcji WaitForSingleObject, wywołuje metodę ReportSvcStatus, aby wskazać, że usługa wprowadziła stan SERVICE_STOPPED i zwraca. (Należy pamiętać, że ważne jest, aby funkcja zwracała, zamiast wywoływać funkcję ExitThread, ponieważ zwracanie umożliwia oczyszczenie pamięci przydzielonej dla argumentów). Dodatkowe zadania oczyszczania można wykonać przy użyciu funkcji RegisterWaitForSingleObject zamiast używać WaitForSingleObject. Wątek z uruchomioną funkcją ServiceMain kończy działanie, ale sama usługa nadal działa. Gdy menedżer kontroli usługi sygnalizuje zdarzenie, wątek z puli wątków wykonuje wywołanie zwrotne w celu dodatkowego czyszczenia, w tym ustawienia stanu na SERVICE_STOPPED.

Należy pamiętać, że w tym przykładzie użyto funkcji SvcReportEvent do zapisywania zdarzeń w dzienniku zdarzeń. Aby uzyskać kod źródłowy dla elementu SvcReportEvent, zobacz Svc.cpp. Aby zapoznać się z przykładową funkcją obsługi kontrolek, zobacz Pisanie funkcji obsługi kontrolek.

Poniższe definicje globalne są używane w tym przykładzie.

#define SVCNAME TEXT("SvcName")

SERVICE_STATUS          gSvcStatus; 
SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
HANDLE                  ghSvcStopEvent = NULL;

Poniższy przykładowy fragment jest pobierany z kompletnego przykładu usługi.

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

Funkcja ServiceMain

Kompletny Przykład Usługi