Запись функции ServiceMain


Функция MyServiceStart в примере ниже является функцией ServiceMain для службы. MyServiceStart имеет доступ к параметрам командной строки, способом, которым это делает главная функция консольного приложения. Первый параметр включает в себя число аргументов, которые передаются службе. По крайней мере, один параметр будет всегда. Второй параметр - это указатель на массив указателей строк. Первый пункт в массиве всегда указывает на имя службы.

Функция MyServiceStart сначала заполняет поля в структуре SERVICE_STATUS, включая управляющие коды, которые она принимает. Хотя эта служба и принимает SERVICE_CONTROL_PAUSE и SERVICE_CONTROL_CONTINUE, она не делает ничего существенного, когда ей говориться, чтобы сделать паузу. Управляющий код SERVICE_ACCEPT_PAUSE_CONTINUE был включен только в целях иллюстрации; если приостановка в работе не добавляет значение вашей службе, не поддерживайте его.

Функция MyServiceStart затем вызывает функцию RegisterServiceCtrlHandler, чтобы зарегистрировать MyService как обработчика функции службы и начать инициализацию. Следующая типовая функция инициализации, MyServiceInitialization, включена в целях иллюстрации; она не выполняет никаких задач инициализации, типа создания дополнительных потоков. Если инициализация вашей службы выполняет задачи, от которых ожидается, что они займут времени дольше, чем одна секунда, ваш код периодически должен вызвать функцию SetServiceStatus, чтобы передавать указания ожидать и контрольные точки, указывающие, что процесс продвигается вперед.

Когда инициализация завершается успешно, в примере вызывается функция SetServiceStatus с состоянием SERVICE_RUNNING и служба продолжает свою работу. Если в инициализации произошла ошибка, MyServiceStart сообщает о SERVICE_STOPPED при помощи функции SetServiceStatus и возвращает значение.

Поскольку эта типовая служба не завершает никаких реальных задач, MyServiceStart просто возвращает  управление вызывающей программе. Однако, ваша служба должна использовать этот поток, чтобы завершить работу независимо от задач, которые были предназначены для выполнения. Если служба не нуждается в потоке, чтобы сделать его работу ( типа службы, которая только обрабатывает запросы RPC), ее функция ServiceMain должна возвратить управление вызывающему потоку. Для функции важно возвратить значение, вместо того, чтобы вызвать функцию ExitThread, потому что возвращение учитывает очистку памяти, выделенной для аргументов.

Вы можете предусмотреть дополнительную очистку, вызывая функцию RegisterWaitForSingleObject на событие перед возвращением значения. Поток, который запускает функцию ServiceMain, завершается, но сама служба продолжает выполняться. Сервисная обрабатывающая программа управления может затем сообщить вашему событию, когда служба остановится, а поток из пула потоков исполняет ваш обратный вызов, чтобы выполнить любую дополнительную очистку, в которой Вы нуждаетесь и вызывает функцию SetServiceStatus с флажком SERVICE_STOPPED. Но, этот прием не иллюстрируется в в типовом коде ниже.

Чтобы выводить на экран  информацию отладки, функция MyServiceStart вызывает SvcDebugOut. Исходный текст для функции SvcDebugOut дается в статье Запись главной функции сервисной программы.

#include <windows.h>

SERVICE_STATUS          MyServiceStatus; 
SERVICE_STATUS_HANDLE   MyServiceStatusHandle; 

VOID SvcDebugOut(LPSTR String, DWORD Status);

void WINAPI MyServiceStart (DWORD argc, LPTSTR *argv) 
{ 
    DWORD status; 
    DWORD specificError; 
 
    MyServiceStatus.dwServiceType        = SERVICE_WIN32; 
    MyServiceStatus.dwCurrentState       = SERVICE_START_PENDING; 
    MyServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP | 
        SERVICE_ACCEPT_PAUSE_CONTINUE; 
    MyServiceStatus.dwWin32ExitCode      = 0; 
    MyServiceStatus.dwServiceSpecificExitCode = 0; 
    MyServiceStatus.dwCheckPoint         = 0; 
    MyServiceStatus.dwWaitHint           = 0; 
 
    MyServiceStatusHandle = RegisterServiceCtrlHandler( 
        "MyService", 
        MyServiceCtrlHandler); 
 
    if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) 
    { 
        SvcDebugOut(" [MY_SERVICE] RegisterServiceCtrlHandler 
            failed %d\n", GetLastError()); 
        return; 
    } 
 
    // Здесь идет код инициализации. 
    status = MyServiceInitialization(argc,argv, &specificError); 
 
    // Ошибочное условие дескриптора 
    if (status != NO_ERROR) 
    { 
        MyServiceStatus.dwCurrentState       = SERVICE_STOPPED; 
        MyServiceStatus.dwCheckPoint         = 0; 
        MyServiceStatus.dwWaitHint           = 0; 
        MyServiceStatus.dwWin32ExitCode      = status; 
        MyServiceStatus.dwServiceSpecificExitCode = specificError; 
 
        SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus); 
        return; 
    } 
 
    // Инициализация завершена - отчет состояния запуска. 
    MyServiceStatus.dwCurrentState       = SERVICE_RUNNING; 
    MyServiceStatus.dwCheckPoint         = 0; 
    MyServiceStatus.dwWaitHint           = 0; 
 
    if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) 
    { 
        status = GetLastError(); 
        SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
            %ld\n",status); 
    } 
 
    // Это - то, где служба делает свою работу. 
    SvcDebugOut(" [MY_SERVICE] Returning the Main Thread \n",0); 
 
    return; 
} 
 
// Функция инициализации заглушки. 
DWORD MyServiceInitialization(DWORD   argc, LPTSTR  *argv, 
    DWORD *specificError) 
{ 
    argv; 
    argc; 
    specificError; 
    return(0); 
}

Назад в оглавление
На главную страницу

Hosted by uCoz