Рейтинговые книги
Читем онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 53 54 55 56 57 58 59 60 61 ... 156

• Все пользователи имеют права SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_ENUMERATE_DEPENDENTS, SERVICE_INTERROGATE и SERVICE_USER_DEFINED_CONTROL;

• Пользователи, входящие в группу Power Users и учетная запись LocalSystem дополнительно имеют права SERVICE_START, SERVICE_PAUSE_CONTINUE и SERVICE_STOP;

• Пользователи, входящие в группы Administrators и System Operators имеют право SERVICE_ALL_ACCESS.

Службы и интерактивность

По умолчанию интерактивные службы могут выполняться только в контексте безопасности LocalSystem. Это связано с особенностями вывода на экран монитора в Windows NT, где существует, например, такой объект как "Desktop", для работы с которым нужно иметь соответствующие права доступа, которых может не оказаться у произвольной учетной записи, отличной от LocalSystem. Несмотря на то, что в подавляющем большинстве случаев это ограничение несущественно однако иногда существует необходимость создать службу, которая выводила бы информацию на экран монитора и при этом выполнялась бы в контексте безопасности отличном от LocalSystem, например, серверная компонента приложения для запуска приложений на удаленном компьютере.

Следующий фрагмент кода иллюстрирует такую возможность.

// Функция, аналог 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);

}

// Функция потока, каждую секунду посылающая уведомления 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);

1 ... 53 54 55 56 57 58 59 60 61 ... 156
На этой странице вы можете бесплатно читать книгу Программирование на Visual C++. Архив рассылки - Алекс Jenter бесплатно.
Похожие на Программирование на Visual C++. Архив рассылки - Алекс Jenter книги

Оставить комментарий