首先,我要感谢所有为本网站工作的人,对开发人员非常有用。这是我从3天开始就被阻止的第一次。我在互联网上搜索过解决方案,但我找不到任何可以解决这个问题的方法。
因此,我开发了一项服务,当用户登录时,必须在vista / seven / 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次,结果总是一样的,非常好......但如果我这样做:
出了点问题,但我找不到解决办法。谢谢你的回答...
答案 0 :(得分:15)
您不需要枚举正在运行的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;
答案 1 :(得分:1)
通过查找“好”的explorer.exe来获取会话ID的方法可能不适合快速用户切换。
尝试让您的应用程序使用WTSRegisterSessionNotification注册会话更改通知。然后,您将在会话切换时收到通知,并使用当前会话ID完成。
请注意以下事项:
要从服务接收会话更改通知,请使用 HandlerEx功能。