Создание сервисов для Windows NT


Приложение, управляющее сервисом Код, описанный в этой статье, работает только в Windows NT / 2000 / XP, поскольку Windows 98 не поддерживает работу с сервисами.

Создание сервиса

Как правило сервис представляет собой консольное приложение, поэтому программа должна содержать функцию main().

Сначала объявим глобальные переменные:

DWORD dwErrCode; //код ошибки
SERVICE_STATUS ss; //текущее сосотояние сервиса
SERVICE_STATUS_HANDLE ssHandle; //дескриптор статуса сервиса

LPTSTR SomeServiceName = "SomeService"; //имя сервиса

Функция main()

void main(int argc, char* argv[]) {
//таблица точек входа
SERVICE_TABLE_ENTRY DispatcherTable[] =
{ { SomeServiceName, //имя сервиса
(LPSERVICE_MAIN_FUNCTION)ServiceMain }, //главная функция сервиса
{ NULL, NULL }
};

Здесь мы создаем таблицу точек входа сервиса.

Структура типа SERVICE_TABLE_ENTRY позволяет задать функцию ServiceMain() для сервиса, носящего указанное в структуре имя.

 //запуск диспетчера
if(!StartServiceCtrlDispatcher(DispatcherTable)) {
printf("StartServiceCtrlDispatcher: Error %ld\n",
GetLastError());
getch();
return;
}
}

Функция StartServiceCtrlDispatcher() связывает главный поток сервиса с менеджером сервисов (service control manager, SCM).

Когда SCM запускает сервис, он ожидает выполнения сервисом функции StartServiceCtrlDispatcher(). Главный поток сервиса должен вызвать эту функцию как можно быстрее. Если функция выполняется успешно, она связывает вызывающий ее поток с SCM и не завершается, пока не будет остановлен сервис. SCM использует это соединение, чтобы посылать сервису управляющие запросы.

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

Функция ServiceMain()

Функция ServiceMain() определяется процессом в качестве точки входа для данного сервиса. Эта функция может носить любое имя, заданное приложением.

void WINAPI ServiceMain(DWORD argc, LPSTR* argv) {
//регистрация управляющей функции сервиса
ssHandle = RegisterServiceCtrlHandler(SomeServiceName, ServiceControl);
if(!ssHandle) {
printf("Error registering ServiceControl\n");
getch();
return;
}

Аргументы функции ServiceMain() (аналогичны аргументам функции main()): dwArgc - количество аргументов, lpszArgv - указатель на массив, который содержит указатели на строковые аргументы функции.

Функция RegisterServiceCtrlHandler() регистрирует функцию, которая будет обрабатывать управляющие запросы к сервису (такие, например, как остановка сервиса). В случае успешного завершения функция возвращает дескриптор статуса сервиса (service status handle). При неудачном завершении функция возвращает нулевое значение.

 //заполняем структуру, определяющую состояние сервиса:
//сервис выполняется как отдельный процесс
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
//устанавливаем состояние ожидания запуска сервиса
SetSomeServiceStatus(SERVICE_START_PENDING, NO_ERROR, 4000);

//инициализация для SomeService
InitSomeServiceData(argc, argv);

//устанавливаем сосотояние работающего сервиса
SetSomeServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);

//основной код программы
//...

return;
}

Поле dwServiceType структуры ss устанавливаем в SERVICE_WIN32_OWN_PROCESS. Это означает, что сервис будет выполняться как отдельный процесс, т.е. будет иметь собственное адресное пространство.

Затем устанавливаем состояние ожидания запуска сервиса c помощью созданной нами вспомогательной функции SetSomeServiceStatus(). Эта функция изменяет содержимое структуры ss, которое использует SCM для получения информации о сервисе.

Далее можно выполнить всю необходимую инициализацию для сервиса (определяемая пользователем функция InitSomeServiceData()).

Когда инициализация выполнена, вызываем функцию SetSomeServiceStatus() с параметром SERVICE_RUNNING, чтобы установить состояние работающего сервиса.

После изменения состояния сервиса выполняется основной код программы.

Функция ServiceControl()

Эта функция является управляющей функцией сервиса и может носить любое имя, определенное приложением.

void WINAPI ServiceControl(DWORD dwControlCode) {
//анализируем код команды и выполняем ее
switch(dwControlCode) {
//команда остановки сервиса
case SERVICE_CONTROL_STOP: {
//устанавливаем состояние ожидания остановки
SetSomeServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

//остановка сервиса
StopSomeService();

//устанавливаем сосотояние остановки сервиса
SetSomeServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
break;
}

С помощью функции SetSomeServiceStatus() мы устанавливаем состояние ожидания остановки сервиса, после чего можно вызвать определяемую пользователем функцию StopSomeService(), которая выполнит все необходимые действия перед остановкой сервиса. Содержимое этой функции зависит от конкретного сервиса.

Далее с помощью функции SetSomeServiceStatus() сообщаем SCM, что сервис остановлен.

 //определение текущего состояния сервиса
case SERVICE_CONTROL_INTERROGATE: {
SetSomeServiceStatus(ss.dwCurrentState, NO_ERROR, 0);
break;
}
default: {
SetSomeServiceStatus(ss.dwCurrentState, NO_ERROR, 0);
break;
}
}
}

Когда сервис получает команду SERVICE_CONTROL_INTERROGATE, это означает, что он должен немедленно обновить информацию о статусе сервиса, используемую SCM.

 
« Предыдущая статья   Следующая статья »