Это действительно возможно. Основная проблема, с которой вы столкнулись, заключается в том, что Windows должна рассматриваться как сервер терминалов, а сеанс пользователей - как удаленный сеанс. Ваша служба должна иметь возможность запускать процесс, который выполняется в удаленном сеансе и принадлежит пользователю.
Кстати, если вы напишите службу, работающую под Windows XP, которая не добавлена в домен, и быстрое переключение пользователей активировано, у вас могут возникнуть такие же проблемы, чтобы запустить процесс при запуске на втором (третьем и и так далее) Рабочий стол зарегистрированных пользователей.
Я надеюсь, что у вас есть токен пользователя, который вы получаете, например, в отношении подражания или у вас есть dwSessionId
сессии. Если у вас его нет, вы можете попробовать использовать какую-либо функцию WTS (API служб удаленных рабочих столов http://msdn.microsoft.com/en-us/library/aa383464.aspx,, например WTSEnumerateProcesses
или WTSGetActiveConsoleSessionId
) или LSA-API, чтобы найти соответствующий сеанс пользователя (LsaEnumerateLogonSessions
см http://msdn.microsoft.com/en-us/library/aa378275.aspx и LsaGetLogonSessionData
см http://msdn.microsoft.com/en-us/library/aa378290.aspx) или ProcessIdToSessionId
(см http://msdn.microsoft.com/en-us/library/aa382990.aspx).
Вы можете использовать функцию GetTokenInformation
с параметром TokenSessionId
(см. http://msdn.microsoft.com/en-us/library/aa446671.aspx) для получения идентификатора сеанса dwSessionId
сеанса пользователя, если вам известен токен пользователя hClient
.
BOOL bSuccess;
HANDLE hProcessToken = NULL, hNewProcessToken = NULL;
DWORD dwSessionId, cbReturnLength;
bSuccess = GetTokenInformation (hClient, TokenSessionId, &dwSessionId,
sizeof(DWORD), &cbReturnLength);
bSuccess = OpenProcessToken (GetCurrentProcess(), MAXIMUM_ALLOWED, &hProcessToken);
bSuccess = DuplicateTokenEx (hProcessToken, MAXIMUM_ALLOWED, NULL,
SecurityImpersonation,
TokenPrimary, &hNewProcessToken);
EnablePrivilege (SE_TCB_NAME);
bSuccess = SetTokenInformation (hNewProcessToken, TokenSessionId, &dwSessionId,
sizeof(DWORD));
bSuccess = CreateProcessAsUser (hNewProcessToken, NULL, szCommandToExecute, ...);
Этот код только схема. EnablePrivilege
- это простая функция, используемая AdjustTokenPrivileges
для включения привилегии SE_TCB_NAME
(см. http://msdn.microsoft.com/en-us/library/aa446619.aspx в качестве шаблона). Важно, чтобы процесс, из которого вы запускаете процесс, имел привилегию TCB, но если ваша служба работает в локальной системе, у вас достаточно разрешений. Кстати, следующий фрагмент кода работает не только с учетной записью Local System, но и учетной записью должна обладать привилегией SE_TCB_NAME
, чтобы иметь возможность переключать текущий сеанс сервера терминалов.
Еще одно замечание. В приведенном выше коде мы запускаем новый процесс с той же учетной записью, что и текущий процесс (например, Local System). Вы изменяете код для использования другой учетной записи, например токена пользователя hClient
. Важно только иметь primary token
. Если у вас есть токен олицетворения, вы можете преобразовать его в основной токен, как показано в коде выше.
В структуре STARTUPINFO
, используемой в CreateProcessAsUser
, следует использовать lpDesktop =
WinSta0 \ Default ".
В зависимости от ваших требований также может потребоваться использовать CreateEnvironmentBlock
для создания нового блока среды, который вы будете передавать новому процессу.
Рекомендую также прочитать Как обеспечить, чтобы окно процесса, запущенное Process.Start (ProcessStartInfo), было в фокусе всех форм? , где я опишу, как заставить процесс запускаться на переднем плане рабочий стол пользователя.