Следующий фрагмент кода иллюстрирует такую возможность.
// Функция, аналог MessageBox Win32 API
int ServerMessageBox(RPC_BINDING_HANDLE h, LPSTR lpszText, LPSTR lpszTitle, UINT fuStyle) {
DWORD dwThreadId;
HWINSTA hwinstaSave;
HDESK hdeskSave;
HWINSTA hwinstaUser;
HDESK hdeskUser;
int result;
// Запоминаем текущие объекты "Window station" и "Desktop".
GetDesktopWindow();
hwinstaSave = GetProcessWindowStation();
dwThreadId = GetCurrentThreadId();
hdeskSave = GetThreadDesktop(dwThreadId);
// Меняем контекст безопасности на тот,
// который есть у вызавшего клиента RPC
// и получаем доступ к пользовательским
// объектам "Window station" и "Desktop".
RpcImpersonateClient(h);
hwinstaUser = OpenWindowStation("WinSta0", FALSE, MAXIMUM_ALLOWED);
if (hwinstaUser == NULL) {
RpcRevertToSelf();
return 0;
}
SetProcessWindowStation(hwinstaUser);
hdeskUser = OpenDesktop("Default", 0, FALSE, MAXIMUM_ALLOWED);
RpcRevertToSelf();
if (hdeskUser == NULL) {
SetProcessWindowStation(hwinstaSave);
CloseWindowStation(hwinstaUser);
return 0;
}
SetThreadDesktop(hdeskUser);
// Выводим обычное текстовое окно.
result = MessageBox(NULL, lpszText, lpszTitle, fuStyle);
// Восстанавливаем сохраненные объекты
// "Window station" и "Desktop".
SetThreadDesktop(hdeskSave);
SetProcessWindowStation(hwinstaSave);
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);
Читать дальше