В дополнение к хабратопику про написание сервисов отмечу еще одну возможность — запуск приложений от имени текущего пользователя терминала.
Это удобно, если выбран вариант управления сервисом и его взаимодействия с пользователем по принципу — сервис не интерактивный и запущен от имени Local System, а приложение должно быть запущено от имени пользователя.
Во-первых, в системе управления сервисами есть механизм позволяющий получить сообщение о подключении нового терминального пользователя и отключении текущего.
Во-вторых, можно запустить любое приложение от имени текущего терминального пользователя.
Лично мне удобнее, когда как сервис, так и пользовательское приложение располагаются в одном исполняемом файле, но это скорее личная прихоть, ни кто не ограничивает программиста в этом.
Начиная с Windows XP/Windows 2003 Server в обработчик событий сервиса может поступить следующее сообщение SERVICE_CONTROL_SESSIONCHANGE, сигнализирующее о смене сессии пользователем (отключение от сессии, создание новой и т.п.) К сожалению в Windows 2000 это не предусмотрено, поэтому (если эта версия значится в списке целевых) следует учитывать, что даже определение этого сообщения в windows.h обрамлено проверкой дефайна _WIN32_WINNT.
В итоге обработчик сообщений дополняется примерно следующим кодом:
Собственно запуск от имени текущего терминального пользователя исполняемого файла сервиса, но с ключем gui:
Остается только напомнить, что у приложения должна быть возможность запуска только одной копии если сервис обрабатывает только WTS_SESSION_LOGON, а для этого достаточно создать именованный мьютекс «имя» которого начинается с L«Local\\». Это позволит создать мьютекс в пределах текущей сессии пользователя. Если же экземпляр приложения должен существовать в принципе в одном экземпляре, то создаем мьютекс «имя» которого начинается с L«Global\\» и не забываем, что в таком случае приложение даже в случае экстренного завершения должно успеть мьютекс удалить.
Это удобно, если выбран вариант управления сервисом и его взаимодействия с пользователем по принципу — сервис не интерактивный и запущен от имени Local System, а приложение должно быть запущено от имени пользователя.
Во-первых, в системе управления сервисами есть механизм позволяющий получить сообщение о подключении нового терминального пользователя и отключении текущего.
Во-вторых, можно запустить любое приложение от имени текущего терминального пользователя.
Лично мне удобнее, когда как сервис, так и пользовательское приложение располагаются в одном исполняемом файле, но это скорее личная прихоть, ни кто не ограничивает программиста в этом.
Начиная с Windows XP/Windows 2003 Server в обработчик событий сервиса может поступить следующее сообщение SERVICE_CONTROL_SESSIONCHANGE, сигнализирующее о смене сессии пользователем (отключение от сессии, создание новой и т.п.) К сожалению в Windows 2000 это не предусмотрено, поэтому (если эта версия значится в списке целевых) следует учитывать, что даже определение этого сообщения в windows.h обрамлено проверкой дефайна _WIN32_WINNT.
В итоге обработчик сообщений дополняется примерно следующим кодом:
Copy Source | Copy HTML
- #if (_WIN32_WINNT > 0x0500)
- case SERVICE_CONTROL_SESSIONCHANGE:
- if (dwEventType == WTS_SESSION_LOGON)
- {
- system_logger_t::instance()->trace_info(L"service accept SESSION LOGON signal");
- if (!run_gui())
- system_logger_t::instance()->trace_info(L"[ERROR] creating GUI window for service failed");
- }
- break;
- #endif
Собственно запуск от имени текущего терминального пользователя исполняемого файла сервиса, но с ключем gui:
Copy Source | Copy HTML
- /*<br/> */
- bool service_t::run_gui()
- {
- HANDLE l_hToken = NULL;
- STARTUPINFOW l_startInfo;
- PROCESS_INFORMATION l_processInfo;
- ULONG l_SID = -1;
- memset(&l_startInfo, 0x00, sizeof(STARTUPINFOW));
- memset(&l_processInfo, 0x00, sizeof(PROCESS_INFORMATION));
- l_startInfo.cb = sizeof(STARTUPINFOW);
- l_SID = static_cast <ULONG> (WTSGetActiveConsoleSessionId());
- if (l_SID != -1)
- {
- if (WTSQueryUserToken(l_SID, &l_hToken) != FALSE)
- {
- std::wstringstream l_wss;
- l_wss << get_path() << get_name();
- if (CreateProcessAsUserW(
- l_hToken,
- l_wss.str().c_str(),
- L" /gui",
- NULL,
- NULL,
- FALSE,
- CREATE_DEFAULT_ERROR_MODE,
- NULL,
- NULL,
- &l_startInfo,
- &l_processInfo) != FALSE )
- {
- system_logger_t::instance()->trace_info(
- L"creating GUI window for service succeeded"
- );
- return true;
- }
- }
- }
- return false;
- }
- //---------------------------------------------------------------------------
Остается только напомнить, что у приложения должна быть возможность запуска только одной копии если сервис обрабатывает только WTS_SESSION_LOGON, а для этого достаточно создать именованный мьютекс «имя» которого начинается с L«Local\\». Это позволит создать мьютекс в пределах текущей сессии пользователя. Если же экземпляр приложения должен существовать в принципе в одном экземпляре, то создаем мьютекс «имя» которого начинается с L«Global\\» и не забываем, что в таком случае приложение даже в случае экстренного завершения должно успеть мьютекс удалить.