当“更改用户”时,CreateProcessAsUser不起作用。 [英] CreateProcessAsUser doesn't work when "change user"

查看:294
本文介绍了当“更改用户”时,CreateProcessAsUser不起作用。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我要感谢所有为该网站工作的人,这对开发人员非常有用。这是我三天以来第一次被阻碍发展。我已经在Internet上搜索了解决方案,但没有找到解决该问题的方法。

First , I want to thank all the persons who works for this site, very useful for a developer. This is the first I am blocked in my developement since 3 days. I have searched solutions on Internet but I find nothing which solves this issue.

因此,我开发了一项服务,该服务必须在vista / seven / xp上执行外部程序用户已登录。这项服务的一些特点:

So, I develop a service which have to execute an external program on vista/seven/xp when a user is logged. Some characteristics of this service :


  • 自动

  • 无交互。

  • 检测已登录用户的会话ID

要以交互用户身份运行外部GUI应用程序:

To run the external GUI application as the interactive user:


  1. 为确保打开用户会话,我列出了所有 explorer.exe进程,并使用msdn函数ProcessIdToSessionId提取其Pid和SessionID

  2. 如果登录用户的SessionID与此 explorer.exe进程的会话ID相等,则我确定好桌面正在运行,因此现在我可以执行外部程序。 (我说好的桌面是因为您知道,可以在系统上打开多个用户会话)

  3. 之后,我使用以下功能运行该应用程序:

  1. To be sure a user session is opened, I list ALL the "explorer.exe" process, extract their Pid and SessionID with the msdn function ProcessIdToSessionId
  2. if the SessionID of the logged user is equal with the session ID of this "explorer.exe" process, I am sure that the "good" desktop is running so now I can execute the external program. (I say "good" desktop because, as you know, more than one user session can be opened on the system )
  3. after that, I run the application with this function:

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;


在大多数情况下,此代码是可以的一:当我改变用户。让我用两个简单的用户(Domain\user1和Domain\user2)来解释一下:

This code is ok in most of case except one: when I change User. Let me explain it with 2 simple users (Domain\user1 and Domain\user2):


  1. 为确保安全,我安装了该服务并重新启动系统

  2. 我与user1打开会话:外部程序已执行,并且可以看到它的形式

  3. 关闭会话并使用user2进行opensession:执行了外部程序,我可以看到它的形式。

  1. To be clean, I install the service and reboot the system
  2. I open session with user1: the external program is executed and I can see its form
  3. I close the session and opensession with user2 : the external program is executed and I can see its the form.

如果我这样做X次,结果总是一样的,非常好...但是,如果我这样做:

If I do this X times, the result is always the same, very good...but If I do this:


  1. 我重新安装服务并重新启动系统

  2. 我打开了与user1的会话:外部程序已执行,我可以看到它的形式

  3. 这次,我不关闭会话,但使用user2 更改用户:执行了外部程序,但我看不到表格,并且发生了错误:系统错误代码5:访问被拒绝。

  1. I re-install the service and reboot the system
  2. I open session with user1: the external program is executed and I can see its form
  3. this time, I am not close the session but change user with user2 : the external program is executed but I cannot see the form and an error is occured : System error code 5: Access denied.

出了点问题,但我找不到解决方案。感谢您的回答...

Something is wrong but I don't find the solution. Thanks for your answers...

推荐答案

您无需枚举正在运行的explorer.exe进程,可以使用 WTSGetActiveConsoleSessionId(),然后将该SessionId传递给 WTSQueryUserToken()。请注意, WTSQueryUserToken()返回模拟令牌,但是 CreateProcessAsUser()需要主令牌,因此请使用 DuplicateTokenEx()进行转换。

You don't need to enumerate running explorer.exe processes, you can use WTSGetActiveConsoleSessionId() instead, and then pass that SessionId to WTSQueryUserToken(). Note that WTSQueryUserToken() returns an impersonation token but CreateProcessAsUser() needs a primary token, so use DuplicateTokenEx() for that conversion.

您还应该使用 CreateEnvironmentBlock(),因此产生的进程具有适合正在使用的用户帐户的适当环境。

You should also use CreateEnvironmentBlock() so the spawned process has a proper environment that is suited to the user account that is being used.

最后,设置 STARTUPINFO.lpDesktop 字段改为'WinSta0\Default'而不是 nil ,因此可以生成生成的UI可以正确看到。

Lastly, set the STARTUPINFO.lpDesktop field to 'WinSta0\Default' instead of nil so the spawned UI can be made visible correctly.

我已经使用这种方法几年了,并且没有任何问题。例如:

I have been using this approach for several years now and have not had any problems with it. For example:

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;

这篇关于当“更改用户”时,CreateProcessAsUser不起作用。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆