从服务启动用户会话中的进程

Windows Vista / 7/2008 / 2008R2中,是否可以通过服务在用户会话中启动进程? 具体来说,本地会话将是最有用的。

我一直在读的东西似乎是说这是不可能的,但我想我会在完全放弃之前问这里。

我在VB.NET编码,但会采取任何build议。

这是真的有可能。 您所遇到的主要问题是Windows应该被视为终端服务器,用户会话被视为远程会话。 您的服务应该能够启动在远程会话中运行的进程属于用户。

顺便说一句,如果你编写的一个服务在Windows XP下运行,而不是添加到一个域,并且激活了快速的用户切换,你可以有同样的问题启动一个进程在第二个(第三个等)上运行,登录用户桌面。

我希望你有一个用户令牌,例如你拿到的模拟方面,或者你有一个dwSessionId的会话。 如果您没有它,可以尝试使用一些WTS功能(远程桌面服务API http://msdn.microsoft.com/en-us/library/aa383464.aspx ,例如WTSEnumerateProcessesWTSGetActiveConsoleSessionId )或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 )。

如果您知道用户令牌hClient则可以使用带有参数TokenSessionId (请参阅http://msdn.microsoft.com/zh-cn/library/aa446671.aspx )的GetTokenInformation函数来接收用户会话的会话ID dwSessionId

 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权限才能切换当前的终端服务器会话。

还有一句话。 在上面的代码中,我们使用与当前进程相同的帐户(例如本地系统)启动新进程。 你改变一个代码来使用另一个帐户,例如用户令牌hClient 有一个primary token只是重要的。 如果您有模拟令牌,则可以将其转换为主令牌,就像上面的代码一样。

CreateProcessAsUser使用的STARTUPINFO结构中,应该使用lpDesktop = WinSta0 \ Default“。

根据您的要求,也可能需要使用CreateEnvironmentBlock创建一个新的环境块,您将传递给新的进程。

我建议你也阅读如何确保由Process.Start(ProcessStartInfo)启动的流程窗口具有所有窗体的焦点? 在这里我描述了如何强制这个过程将在用户桌面的前台开始。

如果有帮助,我面临着类似的问题,但想要一个纯粹的PowerShell解决方案。

我拼凑了一些其他网站的一些东西,想出了这个:

 function Invoke-CommandInSession { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ScriptBlock] $expression ) $commandLine = “powershell“ ## Convert the command into an encoded command for PowerShell $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($expression) $encodedCommand = [Convert]::ToBase64String($commandBytes) $args = “-Output XML -EncodedCommand $encodedCommand” $action = New-ScheduledTaskAction -Execute $commandLine -Argument $args $setting = New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter ([Timespan]::Zero) $trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date) + ([Timespan]::FromSeconds(5))) $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $setting Register-ScheduledTask "Invoke-CommandInSession - $([Guid]::NewGuid())" -InputObject $task } 

是的,它有点奇怪,但它的作品 – 最重要的是,你可以从ps远程调用它

可以这样做,但是它不被认为是服务与用户会话直接交互的良好做法,因为它会造成严重的安全漏洞。

http://support.microsoft.com/kb/327618

更好的方法是创建2个程序,一个后端服务和一个前端客户端UI程序。 服务后端将始终运行,并使用WCF公开其操作(例如)。 客户端程序可以在用户会话启动时运行。

是的,使用CreateProcessAsUser你可以做到这一点。 MSDN有示例文章,并有一些注意事项。

在32位和64位体系结构中颠覆Vista UAC

提供的代码是针对Vista的,但也适用于Win7和Win10