Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.238.198.167] |
|
Сообщ.
#1
,
|
|
|
std::cout << "Привет всем!";
Хочу сделать так, чтобы сервис мог создавать окна GUI. Просто простановка флага SERVICE_INTERACTIVE_PROCESS (вместе с SERVICE_WIN32_OWN_PROCESS) в CreateService ничего не даёт. Подскажите, плиз, каким образом это делается? |
Сообщ.
#2
,
|
|
|
Цитата Jin X @ Просто простановка флага SERVICE_INTERACTIVE_PROCESS (вместе с SERVICE_WIN32_OWN_PROCESS) в CreateService ничего не даёт. А что именно не получается? https://docs.microsoft.com/en-us/windows/de...active-services |
Сообщ.
#3
,
|
|
|
Jin X, создаёшь новую нить, получаешь сессию текущего юзера, которого, к слову, может и не быть, имперсонируешь нитку под этого юзера, внедряешь в его сессию – и вуаля, делов-то . Так ты точно не скомпрометируешь систему.
|
Сообщ.
#4
,
|
|
|
Олег М, ну вот у меня lpServiceStartName = NULL, флаг SERVICE_INTERACTIVE_PROCESS указан, значение NoInteractiveServices в реестре установлено в 0.
В Qraizer, можешь чуть поконкретнее рассказать, как это всё делать? 1. Как получить сессию текущего юзера (без привязки к текущему процессу). 2. Как имперсонировать нитку под сессию? 3. Как внедрить в сессию? |
Сообщ.
#5
,
|
|
|
Цитата Jin X @ Но я тут один, никаких юзеров больше нет. Значит ли это, что я в 0-й сессии? А под какой учётной записью запускается сервис? Вообще, на мой взгляд - не самая лучшая идея делать гуи в сервисе. Проще сделать отдельное приложение, которое будет работать с этим сервисом, например по DCOM |
Сообщ.
#6
,
|
|
|
Jin X, ну, я слишком утрировано описал. Это было возможно в XP. Нынче, нулевая сессия больше недоступна для пользовательских сеансов, так что интерактивные сервисы ты сможешь сделать, только если будешь запускаться под учёткой какого-нибудь пользователя. Вряд ли это проще интеракции с неинтерактивным сервисом, т.к. ты получишь больше проблем с отслеживанием пользовательских сессий, их список же может меняться по ходу дела, + ты получишь те же ограничения, что учётная запись пользователя. В целом же, если ориентироваться на неинтерактивные сессии, Олег М правильно написал:
Цитата Олег М @ . Это не сложно, нужно лишь получить текущую активную сессию пользователя – на сервере терминалов могут быть сложности, там все сессии активны одновременно, так что нужно изобретать другие алгоритмы для выбора сеанса для внедрения – получить токен её юзера и запустить процесс от его имени. Взаимодействие с дочерним процессом наладить можно сильно по-разному, не обязательно DCOM. Вообще, на мой взгляд - не самая лучшая идея делать гуи в сервисе. Проще сделать отдельное приложение, которое будет работать с этим сервисом, например по DCOM Добавлено Как-то так у меня экспериментировалось: unsigned __stdcall threadFunc(void*) { DWORD res; // Ждём сигналов: выход или окошко while ((res = WaitForMultipleObjects(events.size(), &events[0], FALSE, INFINITE)) == WAIT_OBJECT_0+1) { // Подстроиться под текущий логин // Получить текущий сеанс. // Это для WinXP с её Fast User Switch. В реальной терминалсерверной среде лучше оперировать // иными критериями, например, перечислить все сеансы функцией WTSEnumerateSessions() DWORD sessionID = WTSGetActiveConsoleSessionId(); HANDLE ppToken; if (!WTSQueryUserToken(sessionID, &ppToken)) { DWORD res=GetLastError(); MessageBox(NULL, static_cast<std::tostringstream&&>(std::tostringstream() << res << _T(" - WTSQueryUserToken() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK); SetEvent(hStart); continue; } // Подготовка к старту процесса STARTUPINFO startInfo = { sizeof(startInfo), NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }; // Запускаем себя же, но с параметром "startGUI" PROCESS_INFORMATION processInfo; std::tstring commandLine(GetCommandLine()); commandLine += _T(" startGUI\0"); commandLine += _T('\0'); if (!CreateProcessAsUser(ppToken, NULL, &commandLine[0], NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startInfo, &processInfo)) { DWORD res=GetLastError(); MessageBox(NULL, static_cast<std::tostringstream&&>(std::tostringstream() << res << _T(" - CreateProcessAsUser() fail")).str().c_str(), _T("RehostStatus"), MB_SERVICE_NOTIFICATION | MB_OK); SetEvent(hStart); continue; } // Подчищаем за собой CloseHandle(ppToken); CloseHandle(processInfo.hProcess); // Ждём завершения запущенного себя WaitForSingleObject(processInfo.hThread, INFINITE); CloseHandle(processInfo.hThread); // Сигналим главной нити SetEvent(hStart); } return res == WAIT_OBJECT_0; } |
Сообщ.
#7
,
|
|
|
Смотрите. У меня есть сервис и есть конфигуратор. Конфигуратор - это GUI, который меняет параметры. И далее, по сути, ему нужно только уведомить сервис об изменениях, чтобы тот прочёл конфигурацию заново.
Что если сделать 2 отдельные проги: сервис и конфигуратор, а взаимодействие с ними через shared memory сделать? CreateFileMapping + MapViewOfFile + event'ы, к примеру. Это если что-то передать. А если просто уведомить, то и event'ов достаточно... Это нормально будет работать между обычной прогой и сервисом? Добавлено Даже так: вторая прога - это не прога, а батник, который будет запускать прогу с ключом /config, к примеру |
Сообщ.
#8
,
|
|
|
Из проги сигналишь эвентом, который слушает служба. Нормально. Проблема лишь в том, чтобы и прога, и сервис – они оба имели права на доступ в эвенту. Проще всего – прога регистрирует именованный эвент, сервис после проги его открывает по тому же имени, пока та не завершилась и т.с. не убила созданный эвент. Если наоборот, то сервис должен будет заморочиться правильным токеном для эвента, чтобы проге хватило прав его пользовать.
|
Сообщ.
#9
,
|
|
|
Ясно, спасибо, буду экспериментировать
|
Сообщ.
#10
,
|
|
|
Цитата Jin X @ Что если сделать 2 отдельные проги: сервис и конфигуратор, а взаимодействие с ними через shared memory сделать? В сервисе - UDP сервер. В конфигураторе - UDP клиент. Можно будет по-рулить и удалённо. |
Сообщ.
#11
,
|
|
|
Цитата Jin X @ CreateFileMapping + MapViewOfFile Избыточно. Именованного пайпа должно быть достаточно, если приёмник всего один |
Сообщ.
#12
,
|
|
|
Цитата B.V. @ Было бы, конечно, удобнее, если бы именованное что-либо было создано сервисом, а не гуем, т.к. гуй будет запускаться, ясное дело, позже.Именованного пайпа должно быть достаточно, если приёмник всего один Но в принципе, если сделать просто триггер (по сути, мне триггера даже достаточно в данном случае), то можно создать именованный ивент в гуе, а в сервисе периодически пытаться его открыть и проверять наличие "сигнала" в нём. Это прям вообще просто будет. |
Сообщ.
#13
,
|
|
|
Всё правильно говорят - интерактивный GUI должен быть ВНЕ сервиса, потому что Microsoft планомерно перекрывает кислород интерактивным сервисам.
Связь между сервисом и GUI - через любой доступный inter-process communication (IPC): sockets, pipes, memory mapped files. Для уменьшения проблем конфигурирования можно задействовать UPnP. |
Сообщ.
#14
,
|
|
|
Цитата Jin X @ Та можно. Только намаешься токен создавать, который был бы доступен для несервисов. Было бы, конечно, удобнее, если бы именованное что-либо было создано сервисом, а не гуем, т.к. гуй будет запускаться, ясное дело, позже. |
Сообщ.
#15
,
|
|
|
Я же правильно понимаю, что имя event'а нужно обязательно начинать с "Global\" ?
Добавлено Напомню, что ивент создаёт юзерская прога, а сервис потом подцепляет. |