You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以低于调用者的权限调用CreateProcess?——适配管理员程序启动邮件应用与非管理员Outlook交互场景

解决管理员进程以普通用户权限启动子进程的问题

你的推测完全正确——管理员权限的父进程启动的子进程默认会继承高权限令牌,这导致它无法和以普通权限运行的Outlook进行COM交互(UAC的会话隔离机制在起作用)。下面是几种可行的实现方式,尤其适配你用Delphi开发的场景:

方法1:利用进程的Linked Token获取普通权限令牌

当进程通过UAC提升为管理员权限时,系统会为它生成两个令牌:一个是提升后的管理员令牌,另一个是链接的普通用户令牌。我们可以获取这个普通令牌,用它来启动子进程:

Delphi代码示例

uses
  Windows, JclSecurity; // 依赖JCL库,符合你使用TJclEmail的环境

function GetNonElevatedToken(out hNonElevatedToken: THandle): Boolean;
var
  hCurrentToken: THandle;
  TokenLinkedToken: TTokenLinkedToken;
  dwReturnLength: DWORD;
begin
  Result := False;
  hNonElevatedToken := 0;

  // 打开当前进程的令牌
  if not OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY or TOKEN_DUPLICATE or TOKEN_ASSIGN_PRIMARY, hCurrentToken) then
    Exit;
  try
    // 获取链接的普通用户令牌
    if GetTokenInformation(hCurrentToken, TokenLinkedToken, @TokenLinkedToken, SizeOf(TokenLinkedToken), dwReturnLength) then
    begin
      // 复制令牌以获得可用的主令牌
      Result := DuplicateTokenEx(TokenLinkedToken.LinkedToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hNonElevatedToken);
      CloseHandle(TokenLinkedToken.LinkedToken);
    end;
  finally
    CloseHandle(hCurrentToken);
  end;
end;

// 用普通权限启动邮件应用程序
procedure StartMailAppAsNonAdmin(const AppPath: string);
var
  hNonElevatedToken: THandle;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  if not GetNonElevatedToken(hNonElevatedToken) then
    Exit;
  try
    ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
    StartupInfo.cb := SizeOf(StartupInfo);
    // 指定用户默认桌面,避免会话隔离导致窗口无法显示
    StartupInfo.lpDesktop := PChar('winsta0\default');

    // 通过普通令牌启动进程
    if CreateProcessAsUser(hNonElevatedToken, PChar(AppPath), nil, nil, nil, False, 0, nil, nil, StartupInfo, ProcessInfo) then
    begin
      CloseHandle(ProcessInfo.hThread);
      CloseHandle(ProcessInfo.hProcess);
    end;
  finally
    CloseHandle(hNonElevatedToken);
  end;
end;

方法2:借助Explorer.exe的令牌启动(更可靠)

如果Linked Token获取失败(比如进程是直接以管理员身份运行,而非通过UAC提升),可以通过获取系统中explorer.exe的令牌来启动子进程——因为Explorer几乎总是以普通用户权限运行:

核心步骤

  1. 遍历系统进程,找到explorer.exe的主进程(注意排除多开的Explorer窗口进程)
  2. 打开该进程的令牌(需要PROCESS_QUERY_INFORMATION权限)
  3. 复制该令牌,再调用CreateProcessAsUser启动你的邮件应用

关键注意事项

  • 权限前置:你的管理员进程需要临时启用SE_ASSIGNPRIMARY_TOKEN_NAME和SE_INCREASE_QUOTA_NAME权限,否则CreateProcessAsUser会执行失败,可以用JCL的AdjustTokenPrivileges工具函数来实现。
  • 桌面会话:必须指定子进程运行在winsta0\default桌面,否则可能无法正常显示Outlook的邮件表单(管理员进程的桌面与普通用户桌面是隔离的)。
  • 资源清理:所有打开的令牌、进程句柄都要及时关闭,避免系统资源泄漏。

这个方案能确保你的邮件应用以普通用户权限运行,从而正常调用非管理员权限的Outlook邮件表单。

内容的提问来源于stack exchange,提问作者TheItalianDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:34:10