Как получить идентификатор сеанса текущего зарегистрированного пользователя?

Я запускаю процесс изнутри службы 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 Вы не сможете запустить приложение, запущенное службой, для отображения на рабочем столе пользователя.

Другие вопросы по тегам