Вам не нужно перечислять запущенные процессы explorer.exe, вместо этого можно использовать WTSGetActiveConsoleSessionId()
, а затем передать этот SessionId в WTSQueryUserToken()
. Обратите внимание, что WTSQueryUserToken()
возвращает токен олицетворения, но для CreateProcessAsUser()
нужен первичный токен, поэтому используйте DuplicateTokenEx()
для этого преобразования.
Вам также следует использовать CreateEnvironmentBlock()
, чтобы порожденный процесс имел надлежащую среду, подходящую для используемой учетной записи пользователя.
Наконец, установите для поля STARTUPINFO.lpDesktop
значение 'WinSta0\Default'
вместо nil
, чтобы созданный пользовательский интерфейс мог быть правильно отображен.
Я использую этот подход уже несколько лет, и у меня не было никаких проблем с ним. Например:
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;