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

以其他管理员用户运行时获取NSIS安装器的登录用户名

解决NSIS安装器提升权限后写入管理员AppData的问题

这个问题的核心原因很明确:当受限用户通过UAC提升为管理员权限运行安装器时,安装器的进程上下文切换到了管理员用户,此时$PROFILE、$APPDATA这类变量都会指向管理员的用户目录,而不是发起安装请求的受限用户目录。

下面给你两种实用的解决方案,从简单到进阶:

方案一:提前获取原用户路径并传递参数(推荐)

这种方法的思路是先以普通用户身份启动安装器,收集好原用户的AppData路径,再请求管理员权限并传递这个路径,避免上下文切换后丢失原用户信息:

  1. 首先在脚本开头设置安装器默认以普通用户权限运行:

    RequestExecutionLevel user
    
  2. 在安装初始化阶段,先获取当前用户(受限用户)的AppData路径并保存,同时判断是否需要提升权限:

    Var OriginalAppData
    Function .onInit
      ; 获取原用户的AppData路径
      StrCpy $OriginalAppData $APPDATA
      ; 检查当前是否为管理员权限
      AccessControl::IsAdmin
      Pop $0
      If $0 == 0 Then
        ; 不是管理员,重新以管理员身份运行并传递原AppData参数
        ExecShell "runas" "$EXEPATH" "/OriginalAppData=`$OriginalAppData`"
        Quit
      EndIf
    FunctionEnd
    
  3. 在安装器启动时解析命令行参数,拿到传递过来的原AppData路径:

    Function .onInit
      ; 优先读取命令行传递的原AppData路径
      ${GetParameters} $R0
      ${GetOptions} $R0 "/OriginalAppData=" $OriginalAppData
      ; 如果没有参数(比如管理员直接运行),则使用当前进程的$APPDATA
      StrLen $0 $OriginalAppData
      If $0 == 0 Then
        StrCpy $OriginalAppData $APPDATA
      EndIf
    FunctionEnd
    
  4. 后续安装文件到AppData时,直接使用$OriginalAppData替代$PROFILE或$APPDATA:

    Section "Install Files"
      SetOutPath "$OriginalAppData\YourAppFolder"
      File "yourfile.exe"
    SectionEnd
    

方案二:通过Windows API获取原登录用户的Profile路径

如果不想重新启动安装器,可以通过Windows API获取当前登录的交互式用户(即发起安装的受限用户)的Profile路径,再拼接AppData目录。这种方法代码稍复杂,但不需要重启安装器:

Var OriginalUserProfile
Function GetOriginalUserProfile
  ; 获取当前交互式用户的令牌
  System::Call "user32.dll#OpenInputDesktop(i 0, i 0, i 0) p.r0"
  System::Call "user32.dll#GetUserObjectInformation(p r0, i 4, p 0, i 0, *i.r1)"
  System::Call "kernel32.dll#LocalAlloc(i 0, i r1) p.r2"
  System::Call "user32.dll#GetUserObjectInformation(p r0, i 4, p r2, i r1, *i.r3)"
  System::Call "user32.dll#CloseDesktop(p r0)"
  
  ; 通过用户名获取用户SID
  System::Call "advapi32.dll#LookupAccountName(p 0, p r2, p 0, *i.r4, p 0, *i.r5, *i.r6)"
  System::Call "kernel32.dll#LocalAlloc(i 0, i r4) p.r7"
  System::Call "kernel32.dll#LocalAlloc(i 0, i r5) p.r8"
  System::Call "advapi32.dll#LookupAccountName(p 0, p r2, p r7, *i.r4, p r8, *i.r5, *i.r6)"
  
  ; 通过SID获取用户Profile路径
  System::Call "advapi32.dll#GetUserProfileDirectory(p r7, p 0, *i.r9)"
  System::Call "kernel32.dll#LocalAlloc(i 0, i r9) p.r10"
  System::Call "advapi32.dll#GetUserProfileDirectory(p r7, p r10, *i.r9)"
  StrCpy $OriginalUserProfile $r10
  
  ; 释放分配的内存
  System::Call "kernel32.dll#LocalFree(p r2)"
  System::Call "kernel32.dll#LocalFree(p r7)"
  System::Call "kernel32.dll#LocalFree(p r8)"
  System::Call "kernel32.dll#LocalFree(p r10)"
FunctionEnd

; 在初始化阶段调用函数并构造AppData路径
Function .onInit
  Call GetOriginalUserProfile
  StrCpy $OriginalAppData "$OriginalUserProfile\AppData\Roaming"
FunctionEnd

注意:这种方法需要安装器已获得管理员权限,所以脚本开头要设置RequestExecutionLevel admin,或者在需要时触发权限提升。

两种方案里,方案一更简单可靠,尤其是在多用户环境或自定义用户目录的场景下,不容易出现路径解析错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:01:12