2010-06-27 32 views
10

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

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

我在VB.NET中编写代码,但是会在任何事情上采取建议。

+0

哪个用户的SES锡永? – 2010-06-27 16:51:20

+0

本地交互式用户是我关注的内容。我主要关注将服务器锁定在工作站上,但也需要执行其他程序,具体取决于特定条件。 – Brad 2010-06-27 18:13:02

回答

18

确实有可能。您遇到的主要问题是Windows应该被看作是终端服务器,用户会话被视为远程会话。您的服务应该能够启动在属于用户的远程会话中运行的进程。顺便说一下,如果你编写一个在Windows XP下运行的服务,它不会被添加到一个域中并且激活了快速用户切换,那么你可以在启动第二个(第三个等等)登录用户桌面。

我希望你有一个用户令牌,例如你会收到关于模仿的用户令牌,或者你有一个dwSessionId会话。如果没有它,你可以尝试使用一些WTS-功能(远程桌面服务API http://msdn.microsoft.com/en-us/library/aa383464.aspx,例如WTSEnumerateProcessesWTSGetActiveConsoleSessionId)或LSA-API,找出相应的用户会话(LsaEnumerateLogonSessionshttp://msdn.microsoft.com/en-us/library/aa378275.aspxLsaGetLogonSessionData看到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)来接收用户会话的会话ID 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权限的进程是非常重要的,但是如果您的服务在本地系统下运行,则您拥有足够的权限。顺便说一下,下面的代码片段不仅与本地系统帐户一起工作,但该帐户必须具有SE_TCB_NAME权限才能切换当前的终端服务器会话。

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

CreateProcessAsUser使用的STARTUPINFO结构,你应该使用lpDesktop = WinSta0 \ DEFAULT”。

取决于您的需求可能也需要使用CreateEnvironmentBlock来创建你将被传递到新的一个新的环境块流程。

我建议你也读How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms?,我描述了如何给力,这一进程将在用户桌面上的前景开始。

+0

我刚刚解决了一个正在执行此操作的服务问题,并在此处找到了您的答案......非常有帮助。在我的情况下,当我从生成的进程运行文件打开对话框(否则对话框工作正常)时,我得到了一个烦人的“访问被拒绝”错误。调用'CreateEnvironmentBlock'并将结果传递给'CreateProcessAsUser'似乎修复了它。我真的需要设置'STARTUPINFO'结构的'lpDesktop'值吗?我只是把它留作NULL。此外,它的工作没有设置SE_TCB_NAME特权。 – paddy 2012-09-19 05:13:30

+0

@paddy:对不起,但是如果你有一些常见的情况,所有的步骤都是非常必要的。如果服务在某个用户帐户下运行,而不是系统,则必须设置“TokenSessionId”以使交互式用户能够查看已启动进程的窗口。为了能够将'SetTokenInformation'与'TokenSessionId'一起使用,服务帐户确实需要具有'SE_TCB_NAME'特权并且必须启用它。如果屏幕保护程序在进程启动时工作,则应设置'lpDesktop'来查看屏幕保护程序的桌面或默认用户桌面。 – Oleg 2012-09-19 05:34:17

3

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

+0

请不要发布带有版本号的MSDN链接,除非有理由始终使用该版本。 – 2010-06-27 20:34:09

+0

请问,约翰。感谢您的更正。 – holtavolt 2010-06-28 00:07:15

+0

谢谢,我现在正在为.NET的API声明工作。 – Brad 2010-06-30 21:31:49

3

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

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

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

+0

我不相信您链接的文章的内容仍然适用于2008(r2)。会话空间隔离等... – 2010-06-27 17:56:05

+1

这不完全相同的事情。 Brad正在询问如何使用用户凭据创建/ new /进程,而不是在用户会话中执行交互操作作为服务进程。 – 2010-06-27 17:56:20

+1

在用户会话中启动进程仍然与用户的桌面进行交互。让我们不要分开头发,这仍然不是好习惯。链接是最好的,我同意:) – 2010-06-27 18:01:45

4

如果有帮助,我面临着类似的问题,b ut想要一个纯粹的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远程中调用它

相关问题