Inno Setup卸载时如何以原始用户身份运行外部程序?
在Inno Setup卸载阶段以原始用户身份执行PowerShell命令的解决方案
由于Inno Setup卸载程序默认以管理员权限运行,且卸载阶段无法使用ExecAsOriginalUser()函数,要让PowerShell命令以触发卸载的原始用户身份执行,需要通过Windows API获取原始用户的访问令牌,再依托该令牌启动进程。以下是具体实现步骤:
1. 引入必要的Windows API函数
在脚本的[Code]段中添加以下API声明,用于获取用户会话令牌并创建进程:
const WTS_CURRENT_SESSION = $FFFFFFFF; TOKEN_QUERY = $0008; TOKEN_DUPLICATE = $0002; TOKEN_ASSIGN_PRIMARY = $0001; CREATE_NEW_CONSOLE = $00000010; SW_HIDE = 0; type TTokenPrivileges = record PrivilegeCount: DWORD; Privileges: array[0..0] of record Luid: TLargeInteger; Attributes: DWORD; end; end; function WTSGetActiveConsoleSessionId: DWORD; external 'WTSGetActiveConsoleSessionId@kernel32.dll stdcall'; function WTSQueryUserToken(SessionId: DWORD; out Token: THandle): BOOL; external 'WTSQueryUserToken@wtsapi32.dll stdcall'; function DuplicateTokenEx(hExistingToken: THandle; dwDesiredAccess: DWORD; lpTokenAttributes: PSecurityAttributes; ImpersonationLevel: DWORD; TokenType: DWORD; out phNewToken: THandle): BOOL; external 'DuplicateTokenEx@advapi32.dll stdcall'; function CreateProcessAsUserW(hToken: THandle; lpApplicationName: string; lpCommandLine: string; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: string; var lpStartupInfo: TStartupInfoW; var lpProcessInformation: TProcessInformation): BOOL; external 'CreateProcessAsUserW@advapi32.dll stdcall'; function CloseHandle(hObject: THandle): BOOL; external 'CloseHandle@kernel32.dll stdcall';
2. 封装执行命令的函数
添加一个自定义函数,用于以原始用户身份执行指定命令:
function ExecAsOriginalUserUninstall(const Command, Params: string): Boolean; var SessionId: DWORD; UserToken, PrimaryToken: THandle; StartupInfo: TStartupInfoW; ProcessInfo: TProcessInformation; CmdLine: string; begin Result := False; // 获取当前活跃控制台会话ID SessionId := WTSGetActiveConsoleSessionId; if SessionId = WTS_CURRENT_SESSION then Exit; // 获取会话对应的用户令牌 if not WTSQueryUserToken(SessionId, UserToken) then Exit; try // 复制令牌为主要令牌,用于创建进程 if not DuplicateTokenEx(UserToken, TOKEN_QUERY or TOKEN_DUPLICATE or TOKEN_ASSIGN_PRIMARY, nil, SecurityImpersonation, TokenPrimary, PrimaryToken) then Exit; try // 构造完整命令行 CmdLine := Format('"%s" %s', [Command, Params]); // 初始化启动信息 FillChar(StartupInfo, SizeOf(StartupInfo), 0); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.dwFlags := STARTF_USESHOWWINDOW; StartupInfo.wShowWindow := SW_HIDE; // 以原始用户身份创建进程 Result := CreateProcessAsUserW(PrimaryToken, '', CmdLine, nil, nil, False, CREATE_NEW_CONSOLE, nil, '', StartupInfo, ProcessInfo); if Result then begin CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); end; finally CloseHandle(PrimaryToken); end; finally CloseHandle(UserToken); end; end;
3. 在卸载阶段调用函数
可以在CurUninstallStepChanged事件中触发命令执行,确保卸载完成后再恢复扩展:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usPostUninstall then begin // 执行指定的PowerShell命令 ExecAsOriginalUserUninstall('powershell.exe', '-Command "winget install -e -h --id=9PMMSR1CGPWG --source=msstore --accept-package-agreements --accept-source-agreements --disable-interactivity"'); end; end;
注意事项
- 确保脚本在管理员权限下运行(Inno Setup卸载程序默认满足),否则无法调用
WTSQueryUserToken等API。 - 若系统中存在多个活跃会话,
WTSGetActiveConsoleSessionId会返回当前物理控制台对应的会话ID,确保命令在正确的用户环境中执行。 - PowerShell命令参数需要正确转义,避免空格或特殊字符导致命令解析错误。
内容的提问来源于stack exchange,提问作者Jean-Milost Reymond
相关产品推荐
相关产品推荐

