首先,我要感谢所有为这个网站工作的人,对开发人员非常有用。 这是我3天以来第一次受到阻挠。 我在互联网上search的解决scheme,但我找不到解决这个问题。
所以,我开发了一个服务,当用户login时,必须在vista / 7 / xp上执行一个外部程序。 这项服务的一些特点:
以交互式用户身份运行外部GUI应用程序:
之后,我用这个函数运行应用程序:
function RunInteractive(prog_filename: String; sessionID: Cardinal): boolean; var hToken: THandle; si: _STARTUPINFOA; pi: _PROCESS_INFORMATION; begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); SI.lpDesktop := nil; if WTSQueryUserToken(sessionID, hToken) then begin if CreateProcessAsUser(hToken, nil, PChar(prog_filename), nil, nil, False, 0, nil, PChar(ExtractFilePath(prog_filename)), si, pi) then result := true else result := false; end else Begin result := false; End; CloseHandle(hToken); end;
这个代码在大多数情况下都可以,除了一个:当我改变用户。 让我用2个简单的用户(Domain \ user1和Domain \ user2)来解释它:
如果我这样做X次,结果总是一样的,非常好…但如果我这样做:
有什么不对,但我找不到解决办法。 感谢您的回答…
您不需要枚举运行的explorer.exe进程,您可以使用WTSGetActiveConsoleSessionId()
,然后将该SessionId传递给WTSQueryUserToken()
。 请注意, WTSQueryUserToken()
返回模拟令牌,但CreateProcessAsUser()
需要主令牌,因此使用DuplicateTokenEx()
进行该转换。
您还应该使用CreateEnvironmentBlock()
以便生成的进程具有适合正在使用的用户帐户的适当环境。
最后,将STARTUPINFO.lpDesktop
字段设置为'WinSta0\Default'
而不是nil
这样生成的UI可以正确显示。
我已经使用了这种方法好几年了,并没有任何问题。 例如:
function CreateEnvironmentBlock(var lpEnvironment: Pointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; external 'userenv.dll' function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall; external 'userenv.dll'; function RunInteractive(prog_filename: String): Boolean; var hUserToken, hToken: THandle; si: _STARTUPINFOA; pi: _PROCESS_INFORMATION; SessionId: DWORD; Env: Pointer; begin Result := False; ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.lpDesktop := 'WinSta0\Default'; SessionId := WTSGetActiveConsoleSessionId; if SessionId = $FFFFFFFF then Exit; if not WTSQueryUserToken(SessionID, hToken) then Exit; try if not DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserToken) then Exit; finally CloseHandle(hToken); end; try if not CreateEnvironmentBlock(Env, hUserToken, False) then Exit; try Result := CreateProcessAsUser(hUserToken, nil, PChar(prog_filename), nil, nil, False, CREATE_UNICODE_ENVIRONMENT, Env, PChar(ExtractFilePath(prog_filename)), si, pi); if Result then begin CloseHandle(pi.hThread); CloseHandle(pi.hProcess); end; finally DestroyEnvironmentBlock(Env); end; finally CloseHandle(hUserToken); end; end;
可能您通过查找“好”explorer.exe获取会话ID的方法不适用于快速用户切换。
尝试使用WTSRegisterSessionNotification为您的应用程序注册会话更改通知。 当会话切换时,您将收到通知,并附有当前的会话ID。
请注意以下几点:
要从服务接收会话更改通知,请使用HandlerEx函数。