}
// Функция потока, каждую секунду посылающая уведомления 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;
sStatus.dwCheckPoint = 0;
return 0;
}
}
Функция Handler
А вот код функции Handler и вспомогательных потоков:
// Обработчик запросов от SCM
void WINAPI ServiceHandler(DWORD dwCode) {
switch (dwCode) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0, 1000);
hSendStopPending = CreateEvent(NULL, TRUE, FALSE, NULL);
hSendStopThread = CreateThread(NULL, 0, SendStopPending, NULL, 0, & dwThreadId);
SetEvent(hWork);
if (WaitForSingleObject(hServiceThread, 1000) != WAIT_OBJECT_0) {
TerminateThread(hServiceThread, 0);
}
SetEvent(hSendStopPending);
CloseHandle(hServiceThread);
CloseHandle(hWork);
if(WaitForSingleObject(hSendStopThread, 2000) != WAIT_OBJECT_0) {
TerminateThread(hSendStopThread, 0);
}
CloseHandle(hSendStopPending);
sStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(hSS, &sStatus);
break;
case SERVICE_CONTROL_PAUSE:
bPause = true;
sStatus.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(hSS, &sStatus);
break;
case SERVICE_CONTROL_CONTINUE:
bPause = true;
sStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hSS, &sStatus);
break;
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus(hSS, &sStatus);
break;
default:
SetServiceStatus(hSS, &sStatus);
break;
}
}
// Функция потока, аналогичная SendStartPending
// для останова службы.
DWORD WINAPI SendStopPending(LPVOID) {
sStatus.dwCheckPoint = 0;
sStatus.dwCurrentState = SERVICE_STOP_PENDING;
sStatus.dwWaitHint = 2000;
while (true) {
SetServiceStatus(hSS, &sStatus);
sStatus.dwCheckPoint++;
if (WaitForSingleObject(hSendStopPending, 1000) != WAIT_TIMEOUT) break;
}
sStatus.dwCheckPoint = 0;
return 0;
}
Для запросов "Stop" и "Shutdown" используется алгоритм корректного останова службы, аналогичный тому, который используется при старте службы, с той лишь разницей, что вместо параметра SERVICE_START_PENDING в SetserviceStatus передается параметр SERVICE_STOP_PENDING, а вместо SERVICE_RUNNING — SERVICE_STOPPED.
В идеале для запросов "Pause" и "Continue" тоже следует использовать этот подход. Любознательный читатель без труда сможет реализовать его, опираясь на данные примеры.
Заключение
В заключение хотелось бы отметить, что с переходом на Windows 2000 разработка служб не претерпела изменений. Службы по-прежнему остаются важной частью программного обеспечения на платформе Windows, что предоставляет разработчикам широкое поле деятельности.
ВОПРОС-ОТВЕТ
Q. Хотелось бы побольше узнать о предварительном просмотре. В русской программе он смотрится инородным телом на своем иностранном языке. Можно ли его как-то настраивать под себя?
В этой же связи: не могу решить проблему.
В программе 3 меню и, соответственно, 3 панели инструментов, которые создал в Create. Переключая меню, вызываю ShowControlBar – прячу ненужные панели и показываю необходимую. Но после вызова PRINT PREVIEW, в окне появляются сразу все 3 панели инструментов.
Попутно: что означает AFX_IDS_PREVIEW_CLOSE в String Table?
Serg Petukhov
A. Отвечу по порядку.
1. Все языко-зависимые компоненты для печати и предварительного просмотра (панель инструментов, диалог и строки) в соответствии с идеологией MFC оформлены как ресурсы. Эти ресурсы лежат в файле MFC42.DLL, но программа будет искать их там только если они отсутствуют в головной программе. Если же программа статически линкуется с MFC, ресурсы для печати/предварительного просмотра берутся из файла afxprint.rc. Чтобы в этом всём убедиться, достаточно открыть rc-файл, сгенерённым визардом, и найти там строчки:
Читать дальше