Как получить идентификатор сеанса текущего зарегистрированного пользователя?
Я запускаю процесс изнутри службы Windows, используя
ProcessStartInfo processStartInfo = new ....
Process.Start(processStartInfo);
Проблема в том, что если я запускаю службу под локальной системной учетной записью, она работает нормально, но не показывает окно программ. Я пытался поместить учетные данные пользователя в свойствах службы, но затем флажок "Разрешить службе взаимодействовать с рабочим столом" становится отключенным.
Мне действительно нужно запустить приложение, вызывающее его из сервиса, и мне действительно нужно увидеть окно приложения.
Помоги мне, пожалуйста.
UPD. Итак, вы используете перегруженную версию Process.Start, которая принимает имя пользователя, пароль и домен - она перетянет программу на рабочий стол. Но теперь оно запускает приложение под одной учетной записью, но показывает это на рабочем столе другого пользователя. Как так?
UPD2: у меня есть идея! Я могу использовать psexec.exe из Sysinternals Suite. Но проблема в том, что мне нужно запустить эту вещь тихо "как администратор". И я не знаю как. Я имею в виду, даже если у вас уже есть права администратора, иногда вы должны вручную сказать "запустить от имени администратора", подтвердить UAC и только после этого вы готовы к работе. Я не знаю, как тихо запустить что-то, не принося UAC вещь....
UPD3: Дорогой Господь. У меня есть эта вещь! В заключение.
Хорошо. В начале проблема действительно была в сеансе 0, что-то изолированное. Поэтому мне нужно было создать промежуточное приложение, которое можно запустить из службы, а затем это приложение, в свою очередь, предполагает запустить мое приложение через RPC и перенести его на рабочий стол. Вместо того, чтобы создавать приложение среднего уровня, я решил использовать инструмент psexec (в любом случае он работает именно так, как мне нужно - через RPC). И когда я попытался использовать этот инструмент под учетной записью LOCAL SYSTEM, он почему-то не работал. И тогда я понял - причина в том, что это чертовски всплывающее диалоговое окно EULA, которое MS вставляет в каждый отдельный pstool, и было невозможно нажать кнопку, чтобы подтвердить диалог под учетной записью локальной системы. Поэтому решение заключается в создании ключа в реестре HKU.DEFAULT\Software\Sysinternals\PsExec со значением DWORD EulaAccepted = 1.
Ура, теперь это работает! НО! Теперь мне нужно вывести программу на экран текущего зарегистрированного пользователя. Для этого мне понадобится идентификатор сессии!
Таким образом, вопрос: как получить идентификатор сеанса текущего зарегистрированного пользователя? А что будет, если еще никто не вошел? Какой идентификатор сессии это будет?
UPD4: Вот и все! Я получил это!
[DllImport ("Kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId")] public static extern int WTSGetActiveConsoleSessionId ();
Спасибо вам, ребята!
4 ответа
Одним из решений было бы использование третьего процесса в качестве посредника и указание ему запускать приложения через RPC/ именованные каналы.
Процессы:
- Служба Windows
- Посредническая заявка
- Приложение, которое вы хотите запустить
Shim создает конечную точку связи (именованный канал, конечная точка WCF) и прослушивает ее. Когда он получает сообщение, запускается приложение, которое вы хотите запустить.
Затем, когда служба Windows хочет запустить приложение, она находит и открывает конечную точку (именованный канал, конечная точка WCF) и отправляет сообщение для запуска приложения. Затем промежуточное приложение заботится о бизнесе, запускающем процесс, и не имеет никаких ограничений, которые имеет служба Windows.
Начните этот посреднический процесс с входа в систему, и все готово.
Это похоже на работу тестового агента / контроллера Microsoft, когда вам нужно запустить тесты, которые взаимодействуют с рабочим столом.
Вы можете получить активный идентификатор сеанса консоли, используя WTSGetActiveConsoleSessionId (из API служб терминалов). Вы можете использовать его только для WinXP/Win2K3 или выше, но это должно быть хорошо, так как вы можете жестко кодировать 0 для идентификатора сеанса на Win2K или более ранней версии. Вот подпись PInvoke для этого:
[DllImport("Kernel32.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.U4)]
public static extern int WTSGetActiveConsoleSessionId ( );
Что касается запуска процесса в сеансе пользователя, вы можете сослаться на ответ, который я дал здесь. В основном это связано с вызовом четырех API; WTSGEtConsoleSessionId, WTSQueryUserToken, DuplicateTokenEx, затем CreateProcessAsUser, и он будет работать на любой машине с WinXP/Win2K3 или выше.
Это можно сделать без промежуточного процесса, но для этого требуется более 500 строк кода. По сути, вы хотите запустить второй процесс как текущий вошедший в систему пользователь. Для Vista/7 у этого пользователя будет свой собственный процесс winlogon, а для XP у него будет процесс проводника. Вам нужно получить основной токен, блок среды, атрибуты безопасности и атрибуты безопасности потока этого запущенного процесса, а затем вызвать функцию API Windows CreateProcessAsUser со всей этой информацией, убедившись, что вы также выбрали правильную станцию окна (обычно "WinSta0\Default"). "). Это все выполнимо, но вы могли бы лучше провести время с другим предложением второго процесса и IPC.
Если вы попробуете это на чем-то более новом, чем WindowsXP, это не сработает. Это происходит из-за новой функции, представленной в Vista / Windows 7, называемой изоляцией сеанса 0. http://msdn.microsoft.com/en-us/library/bb756986.aspx Вы не сможете запустить приложение, запущенное службой, для отображения на рабочем столе пользователя.