在 Windows Vista/7/2008/2008R2 中,是否可以通过服务在用户 session 中启动进程?具体而言,本地 session 最有用。
我一直在阅读的所有内容似乎都在说这是不可能的,但我想在完全放弃之前我会在这里问一下。
我正在用 VB.NET 编写代码,但会接受任何建议。
最佳答案
这真的有可能。您遇到的主要问题是 Windows 应被视为终端服务器,而用户 session 应被视为远程 session 。您的服务应该能够启动在属于用户的远程 session 中运行的进程。
顺便说一句,如果您编写的服务在未添加到域的 Windows XP 下运行并且激活了快速用户切换,则在第二个(第三个和第二个)上启动进程时可能会遇到相同的问题等等)登录用户桌面。
我希望您有一个用户 token ,例如您收到关于模拟的用户 token ,或者您有一个 dwSessionId
session 。如果您没有它,您可以尝试使用一些 WTS 函数(远程桌面服务 API http://msdn.microsoft.com/en-us/library/aa383464.aspx,例如 WTSEnumerateProcesses
或 WTSGetActiveConsoleSessionId
)或 LSA-API 来查找输出相应的用户 session (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 )。
您可以使用带有参数 TokenSessionId
的 GetTokenInformation
函数(参见 http://msdn.microsoft.com/en-us/library/aa446671.aspx )来接收用户 session 的 session ID dwSessionId
如果你知道用户 token 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 权限,但如果您的服务在本地系统下运行,您就有足够的权限。顺便说一下,下面的代码片段不仅适用于本地系统帐户,而且该帐户必须具有 SE_TCB_NAME
权限才能切换当前终端服务器 session 。
再多说一句。在上面的代码中,我们使用与当前进程相同的帐户(例如本地系统)启动新进程。您更改代码以使用另一个帐户,例如用户 token hClient
。唯一重要的是拥有一个 primary token
。如果您有模拟 token ,您可以将其转换为主 token ,就像上面的代码一样。
在 CreateProcessAsUser
中使用的 STARTUPINFO
结构中,您应该使用 lpDesktop =
WinSta0\Default"。
根据您的要求,可能还需要使用 CreateEnvironmentBlock
创建一个您将传递给新进程的新环境 block 。
我建议您也阅读 How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms?我在其中描述了如何强制该进程在用户桌面的前台启动。
关于c# - 从服务启动用户 session 中的进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3128017/