Electron-builder:以管理员身份安装时指向当前用户LocalAppdata的方案
问题:管理员身份运行安装程序时,Electron应用安装到错误的LocalAppData目录
我开发了一个Electron应用,计划将其安装到当前用户的$LocalAppData目录下,当前Electron-builder的NSIS配置如下:
"nsis": { "oneClick": true, "perMachine": false, "createDesktopShortcut": true, "createStartMenuShortcut": true, "runAfterFinish": false, "include": "installer.nsh" }
配套的installer.nsh内容为:
!macro preInit SetRegView 64 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppdata\Programs\My App" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppdata\Programs\My App" SetRegView 32 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppdata\Programs\My App" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppdata\Programs\My App" !macroend
遇到的问题:
- 普通用户或管理员直接运行安装程序时,应用能正常安装到
$LocalAppdata\Programs\My App目录; - 但普通用户右键选择「以管理员身份运行」安装程序时,应用会被安装到管理员账户的
$LocalAppdata目录,而非当前用户的目录。尝试将$LocalAppdata替换为$PROFILE也无法解决。
请问能否通过.nsh脚本实现将应用安装到当前用户的$LocalAppdata目录?
解决方案
问题根源在于:当以管理员身份运行安装程序时,进程运行在管理员的安全上下文下,此时环境变量$LocalAppData指向的是管理员账户的目录,而非发起操作的普通用户目录。要解决这个问题,需要在NSIS脚本中获取当前交互式登录用户的LocalAppData路径,而非运行进程的用户路径。
以下提供两种可靠的实现方式:
方式1:使用Windows API获取已知文件夹路径(推荐)
替换installer.nsh中的preInit宏为以下代码:
!macro preInit Var CurrentUserLocalAppData ; 调用SHGetKnownFolderPath获取当前用户的LocalAppData目录(FOLDERID_LocalAppData) System::Call 'shell32.dll::SHGetKnownFolderPath(g "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}", i 0, i 0, *p.r0)' ; 转换宽字符到多字节字符串 System::Call 'user32.dll::MultiByteToWideChar(i 0, i 0, t r0, i -1, *t.r1, i 0)' StrCpy $CurrentUserLocalAppData $r1 System::Free $r0 ; 设置安装目录为当前用户的LocalAppData下的目标路径 StrCpy $INSTDIR "$CurrentUserLocalAppData\Programs\My App" ; 写入注册表记录安装路径 SetRegView 64 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" SetRegView 32 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" !macroend
方式2:通过注册表读取当前用户SID获取路径
如果系统兼容性要求更高,可以通过读取当前用户的SID来获取Profile路径,再拼接LocalAppData:
!macro preInit Var CurrentUserLocalAppData Var UserSID ; 获取当前进程令牌并读取SID System::Call 'advapi32.dll::OpenProcessToken(i -1, i 0x0008, *i.r0)' System::Call 'advapi32.dll::GetTokenInformation(i r0, i 1, *i.r1, i 4, *i.r2)' System::Call 'advapi32.dll::ConvertSidToStringSidA(i r1, *t.r2)' StrCpy $UserSID $r2 System::Free r1 System::Call 'advapi32.dll::CloseHandle(i r0)' ; 从当前用户的注册表项读取LocalAppData ReadRegStr $CurrentUserLocalAppData HKU "${UserSID}\Environment" LocalAppData ; 如果读取失败,从Profile路径拼接LocalAppData ${If} $CurrentUserLocalAppData == "" ReadRegStr $CurrentUserLocalAppData HKU "${UserSID}" "ProfileImagePath" StrCpy $CurrentUserLocalAppData "$CurrentUserLocalAppData\AppData\Local" ${EndIf} ; 设置安装目录 StrCpy $INSTDIR "$CurrentUserLocalAppData\Programs\My App" ; 写入注册表 SetRegView 64 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" SetRegView 32 WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR" !macroend
内容的提问来源于stack exchange,提问作者Algorys
相关产品推荐
相关产品推荐

