Чтение онлайн

ЖАНРЫ

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

 CloseDesktop(hdeskUser);

 CloseWindowStation(hwinstaUser);

 return result;

}

В этом фрагменте в ответ на запрос, посланный клиентской частью приложения последством RPC, служба выводит текстовое сообщение на экран монитора.

Пример службы (ключевые фрагменты)

Рассмотрим на примере ключевые фрагменты приложения на языке C++, реализующего службу Windows NT. Для наглядности несущественные части кода опущены.

Функция main

Вот как выглядит код

функции main:

void main {

 SERVICE_TABLE_ENTRY steTable[] = {

{SERVICENAME, ServiceMain}, {NULL, NULL}

 };

 // Устанавливаем соединение с SCM. Внутри этой функции

 // происходит прием и диспетчеризация запросов.

 StartServiceCtrlDispatcher(steTable);

}

Функция ServiceMain

Особенностью кода, содержащегося в ServiceMain, является то, что часто невозможно заранее предсказать время выполнения той или иной операции, особенно, если учесть, что ее выполнение происходит в операционной системе с вытесняющей многозадачностью. Если операция продлится дольше указанного в параметре вызова SetServiceStatus интервала времени, служба не сможет во-время отправить следующее уведомление, в результате чего SCM остановит ее работу. Примерами потенциально операций могут служить вызовы функций работы с сетью при больших таймаутах или единовременное чтение большого количества информации с медленного носителя. Кроме того, такой подход совершенно не применим при отладке службы, поскольку выполнение программы в отладчике сопровождается большими паузами, необходимыми разработчику.

Для преодоления этой проблемы все операции по взаимодействию с SCM следует выполнять в отдельном потоке, не зависящем от действий, происходящих на этапе инициализации.

Алгоритм корректного запуска службы, использующий вспомогательный поток:

void WINAPI ServiceMain(DWORD dwArgc, LPSTR *psArgv) {

 // Сразу регистрируем обработчик запросов.

 hSS = RegisterServiceCtrlHandler(SERVICENAME, ServiceHandler);

 sStatus.dwCheckPoint = 0;

 sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;

 sStatus.dwServiceSpecificExitCode = 0;

 sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

 sStatus.dwWaitHint = 0;

 sStatus.dwWin32ExitCode = NOERROR;

 // Для инициализации службы вызывается функция InitService;

 // Для того, чтобы в процессе инициализации система не

 // выгрузила службу, запускается поток, который раз в

 // секунду сообщает, что служба в процессе инициализации.

 // Для синхронизации потока создаётся событие.

 // После этого запускается рабочий поток, для

 // синхронизации которого также

 // создаётся событие.

 hSendStartPending = CreateEvent(NULL, TRUE, FALSE, NULL);

 HANDLE hSendStartThread;

 DWORD dwThreadId;

 hSendStartThread = CreateThread(NULL, 0, SendStartPending, NULL, 0, &dwThreadId);

 //Здесь
производится вся инициализация службы.

 InitService;

 SetEvent(hSendStartPending);

 if (WaitForSingleObject(hSendStartThread, 2000) != WAIT_OBJECT_0) {

TerminateThread(hSendStartThread, 0);

 }

 CloseHandle(hSendStartPending);

 CloseHandle(hSendStartThread);

 hWork = CreateEvent(NULL, TRUE, FALSE, NULL);

 hServiceThread = CreateThread(NULL, 0, ServiceFunc, 0, 0, &dwThreadId);

 sStatus.dwCurrentState = SERVICE_RUNNING;

 SetServiceStatus(hSS, &sStatus);

}

// Функция потока, каждую секунду посылающая уведомления SCM

// о том, что процесс инициализации идёт. Работа функции

// завершается, когда устанавливается

// событие hSendStartPending.

DWORD WINAPI SendStartPending(LPVOID) {

 sStatus.dwCheckPoint = 0;

 sStatus.dwCurrentState = SERVICE_START_PENDING;

 sStatus.dwWaitHint = 2000;

 // "Засыпаем" на 1 секунду. Если через 1 секунду

 // событие hSendStartPending не перешло

 // в сигнальное состояние (инициализация службы не

 // закончилась), посылаем очередное уведомление,

 // установив максимальный интервал времени

 // в 2 секунды, для того, чтобы был запас времени до

 // следующего уведомления.

 while (true) {

SetServiceStatus(hSS, &sStatus);

sStatus.dwCheckPoint++;

if (WaitForSingleObject(hSendStartPending, 1000) != WAIT_TIMEOUT) break;

 }

 sStatus.dwCheckPoint = 0;

 return 0;

}

// Функция, инициализирующая службу. Чтение данных,

// распределение памяти и т.п.

void InitService {

 ...

}

// Функция, содержащая «полезный» код службы.

DWORD WINAPI ServiceFunc(LPVOID) {

 while (true) {

if (!bPause) {

// Здесь содержится код, который как правило

// выполняет какие-либо циклические операции...

}

if (WaitForSingleObject(hWork, 1000) != WAIT_TIMEOUT) break;

Поделиться с друзьями: