fix(installer): 安装位置识别与模式切换目录(真机四因根除)
主人真机复验:① 不能像原版那样自动识别「已安装程序的安装位置」;② 选「为所有用户安装」却装到
%LOCALAPPDATA% 而不是 Program Files。
真机打点(instrumented 副本 + WM_COMMAND/BM_CLICK/WM_SETTEXT 驱动)读出四个根因:
1. 切换安装模式单选钮只改状态位图,**不同步输入框**;
2. setInstallModePerAllUsers/PerUser 会按记忆/默认**重算 $INSTDIR**,把「框里定下的值」
(用户手改、浏览选择、记忆)**无条件冲掉** —— 这是两条症状的共同根因
(打点:after-normalize=[D:\SBCustom\Scoreboard Console] → after-mode=[C:\Program Files (x86)\…]);
3. 记忆读取 HKCU 优先、**不看安装模式** → 一条更早的用户级记忆顶掉刚装好的机器级目录;
4. 无人值守会话里 $LOCALAPPDATA 解析成 C:\ProgramData →「仅为我」默认成了 C:\ProgramData\Programs\…。
改法:
- 新增 ModeDefaultDir:「为所有用户」→ $PROGRAMFILES64\${APP_FILENAME}(本包是 x64);
「仅为我」→ SHGetKnownFolderPath(FOLDERID_UserProgramFiles),失败才退回 $LOCALAPPDATA\Programs;
- 新增 PathTouched(框里的文本 != 程序设定的值 = 用户手改过)与 SyncPathToMode:
切模式时**只有用户没改过**才把框与 $INSTDIR 同步成该模式默认目录(不冲掉用户刚填的);
- PageDirLeave 用 $pathKeep 保住路径,跑完上游两个宏后再恢复 —— 只让宏改模式与 SHELL_CONTEXT,不再改路径;
- .onInit 在 customInit 之后**按当前模式**重读自家记忆键(all → HKLM,否则 HKCU)。
真机六条全过(含落盘复核):进 P3 显示记忆目录;点「为所有用户」→ C:\Program Files\Scoreboard_Console;
点「仅为我」→ %LOCALAPPDATA%\Programs\…;点回去再正确切换;手塞自定义路径以它为准;改过后再切模式不被冲掉。
静默无回归:/S 6.6s exit=0 落记忆目录;/S /D= 6.4s exit=0 且 /D= 优先。
wine 回归:人手速度拖动、原生控件隐藏、P3→P5 自动翻页、✕ 退出无 StdUtils 报错、旧版在跑时 /S 关掉旧进程。
This commit is contained in:
1 parent
9951a69938
commit
a57df78f99
1 file changed
+99
-2
@@ -162,6 +162,8 @@ Var dragWy
|
||||
Var gDparam ; 命令行 /D= 指定的安装目录(要在 customInit 的记忆目录之后盖回去)
|
||||
Var keepShortcuts ; 快捷方式保留策略,由安装流程写定(上游在安装段里声明,我们提前到全局)
|
||||
Var dragging
|
||||
Var pathSet ; 我们最后一次写进「安装位置」输入框的值(用来判断用户有没有手改过)
|
||||
Var pathKeep ; 计算路径时临时存一份(上游那两个宏会重写 $INSTDIR,用它盖回来)
|
||||
|
||||
|
||||
; ── include ────────────────────────────────────────────────────────────────
|
||||
@@ -1183,16 +1185,82 @@ Function OnLicNext
|
||||
Call GoNext
|
||||
FunctionEnd
|
||||
|
||||
; 当前安装模式对应的「默认目录」(结果放 $R0)。
|
||||
; 「为所有用户」= 公共目录:本包是 x64 应用,所以优先 $PROGRAMFILES64(真机期望就是 C:\Program Files\…),
|
||||
; 32 位系统上退回 $PROGRAMFILES;「仅为我」= %LOCALAPPDATA%\Programs\…(与上游 setInstallModePerUser 一致)。
|
||||
Function ModeDefaultDir
|
||||
${If} $modeAll == 1
|
||||
StrCpy $R0 "$PROGRAMFILES"
|
||||
!ifdef APP_64
|
||||
${If} ${RunningX64}
|
||||
StrCpy $R0 "$PROGRAMFILES64"
|
||||
${EndIf}
|
||||
!endif
|
||||
StrCpy $R0 "$R0\${APP_FILENAME}"
|
||||
${Else}
|
||||
; 与上游 setInstallModePerUser 同源:优先问 shell 要「用户程序目录」(FOLDERID_UserProgramFiles)。
|
||||
; 不能直接用 $LOCALAPPDATA:在提权/无人值守(session 0,没有加载用户配置)的会话里,
|
||||
; NSIS 的 $LOCALAPPDATA 会解析成 C:\ProgramData —— 真机实测就是装到了
|
||||
; 「C:\ProgramData\Programs\Scoreboard_Console」,那不是用户的程序目录。
|
||||
StrCpy $R0 "$LOCALAPPDATA\Programs"
|
||||
Push $0
|
||||
Push $1
|
||||
Push $2
|
||||
StrCpy $2 0
|
||||
System::Call 'SHELL32::SHGetKnownFolderPath(g "${FOLDERID_UserProgramFiles}", i ${KF_FLAG_CREATE}, p 0, *p .r2) i .r1'
|
||||
${If} $1 == 0
|
||||
System::Call 'KERNEL32::lstrcpynW(w .r0, p r2, i ${NSIS_MAX_STRLEN}) p'
|
||||
StrCpy $R0 "$0"
|
||||
${EndIf}
|
||||
${If} $2 != 0
|
||||
System::Call 'OLE32::CoTaskMemFree(p r2)'
|
||||
${EndIf}
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
StrCpy $R0 "$R0\${APP_FILENAME}"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; 用户有没有手改过「安装位置」框?结果放 $R9(1 = 改过)。
|
||||
; 判据是「框里的文本 != 我们最后一次写进去的值」——不需要额外的控件通知,浏览/手输都盖得住。
|
||||
Function PathTouched
|
||||
${NSD_GetText} $hPath $R1
|
||||
StrCpy $R9 0
|
||||
${If} $R1 != $pathSet
|
||||
StrCpy $R9 1
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; 切模式时把路径与输入框同步到该模式的默认目录。
|
||||
; 策略(说得通的理由):**用户没手改过**才跟着模式变;用户改过就保持他填的,
|
||||
; 免得「改完路径再点一下单选」把用户刚填的东西悄悄冲掉。
|
||||
; 之前这里只改单选状态位图、不动框 —— 于是「选了为所有用户,框里还是用户目录」,
|
||||
; 真机复验的两条症状(记忆目录看不出、选公共安装却像在装用户目录)都是它引起的。
|
||||
Function SyncPathToMode
|
||||
Call PathTouched
|
||||
${If} $R9 == 0
|
||||
Call ModeDefaultDir
|
||||
StrCpy $INSTDIR $R0
|
||||
${If} $hPath != 0
|
||||
${NSD_SetText} $hPath "$INSTDIR"
|
||||
StrCpy $pathSet "$INSTDIR"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function OnModeUser
|
||||
StrCpy $modeAll 0
|
||||
!insertmacro SetBmpBase $hRadio1 "radio-on.bmp" 16 16
|
||||
!insertmacro SetBmpBase $hRadio2 "radio-off.bmp" 16 16
|
||||
Call SyncPathToMode
|
||||
FunctionEnd
|
||||
|
||||
Function OnModeAll
|
||||
StrCpy $modeAll 1
|
||||
!insertmacro SetBmpBase $hRadio1 "radio-off.bmp" 16 16
|
||||
!insertmacro SetBmpBase $hRadio2 "radio-on.bmp" 16 16
|
||||
Call SyncPathToMode
|
||||
FunctionEnd
|
||||
|
||||
; 「浏览…」:系统文件夹选择框没有 NSIS 包装,用 System::Call 直接调 shell32。
|
||||
@@ -1213,6 +1281,8 @@ Function OnBrowse
|
||||
${If} $R2 != ""
|
||||
StrCpy $tmpTxt $R2
|
||||
${NSD_SetText} $hPath "$tmpTxt"
|
||||
; 浏览选出来的目录也算用户改过:别让切模式把它冲掉
|
||||
StrCpy $pathSet ""
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
System::Free $R7
|
||||
@@ -1365,7 +1435,11 @@ Function PageDir
|
||||
Call Chrome
|
||||
|
||||
!insertmacro NewEdit "${ES_AUTOHSCROLL}|${WS_CHILD}|${WS_VISIBLE}" ${PATH_EDIT_X} ${PATH_EDIT_Y} ${PATH_EDIT_W} ${PATH_EDIT_H} $hPath
|
||||
; 进页面时框里显示的就是当前 $INSTDIR:initMultiUser + customInit 已经把「上次装到哪」
|
||||
; (我们自己的记忆键,HKCU 优先、再 HKLM)读进来了,所以这里就是「记忆目录」。
|
||||
; $pathSet 记下我们写的这份值,之后 SyncPathToMode / PageDirLeave 靠它判断用户有没有手改过。
|
||||
${NSD_SetText} $hPath "$INSTDIR"
|
||||
StrCpy $pathSet "$INSTDIR"
|
||||
!insertmacro NewBmpBtn ${BROWSE_X} ${BROWSE_Y} ${BROWSE_W} ${BROWSE_H} $hBrowse "btn-browse-normal.bmp"
|
||||
${NSD_OnClick} $hBrowse OnBrowse
|
||||
|
||||
@@ -1414,12 +1488,17 @@ Function PageDirLeave
|
||||
StrCpy $INSTDIR "$INSTDIR\${APP_FILENAME}"
|
||||
${EndIf}
|
||||
|
||||
; 兜底:万一路径是空的(记忆为空 + 默认值也没算出来),给一个合法的按用户目录,
|
||||
; 兜底:万一路径是空的(记忆为空 + 默认值也没算出来),按当前模式给一个合法默认目录,
|
||||
; 绝不把空 $INSTDIR 带进安装流程(空路径是「点了没反应 / 装到莫名其妙的地方」的经典来源)。
|
||||
; 走 ModeDefaultDir 而不是直接拼 $LOCALAPPDATA —— 后者在无人值守会话里会解析成 C:\ProgramData。
|
||||
${If} $INSTDIR == ""
|
||||
StrCpy $INSTDIR "$LOCALAPPDATA\Programs\${APP_FILENAME}"
|
||||
Call ModeDefaultDir
|
||||
StrCpy $INSTDIR "$R0"
|
||||
${EndIf}
|
||||
|
||||
; 先把定下来的路径存一份:下面这两个上游宏会按注册表记忆/模式默认**重新算 $INSTDIR**,
|
||||
; 而这一页的规矩是「框里是什么就装什么」(记忆、模式默认、用户手改、/D= 都已经体现在框里了)。
|
||||
StrCpy $pathKeep "$INSTDIR"
|
||||
${If} $modeAll == 1
|
||||
; 「为所有用户安装」。执行级别是 highest,所以**管理员账户的机器上启动时就提过权了**,
|
||||
; 这里不需要(也不该)再走一次 UAC_RunElevated:中途提权会把窗口藏起来、让用户看见
|
||||
@@ -1436,6 +1515,8 @@ Function PageDirLeave
|
||||
${Else}
|
||||
!insertmacro setInstallModePerUser
|
||||
${EndIf}
|
||||
; 盖回框里定下的路径($installMode / SHELL_CONTEXT 已经在宏里设好了,这里只保路径)
|
||||
StrCpy $INSTDIR "$pathKeep"
|
||||
FunctionEnd
|
||||
|
||||
; ── P4 正在安装 ────────────────────────────────────────────────────────────
|
||||
@@ -1572,6 +1653,22 @@ Function .onInit
|
||||
!ifmacrodef customInit
|
||||
!insertmacro customInit
|
||||
!endif
|
||||
; 记忆目录要跟**安装模式**对齐,这才是「原版安装器」的行为:initMultiUser 读的就是该模式对应的键
|
||||
; (为所有用户 → HKLM,仅为我 → HKCU)。构建期生成的 customInit 是「HKCU 的自家记忆优先」,
|
||||
; 于是一条更早的用户级记忆会把刚装好的机器级目录顶掉 —— 真机实测:机器上最后一次装在
|
||||
; C:\Program Files\Scoreboard_Console,安装位置页却填着更早的 D:\Program Files\Scoreboard_Console。
|
||||
; 这里按当前模式再读一遍自家记忆键,有值就盖回去;两个 hive 都空时保留 initMultiUser/customInit 的结果。
|
||||
${If} $installMode == "all"
|
||||
ReadRegStr $R9 HKLM "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"
|
||||
${If} $R9 != ""
|
||||
StrCpy $INSTDIR $R9
|
||||
${EndIf}
|
||||
${Else}
|
||||
ReadRegStr $R9 HKCU "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"
|
||||
${If} $R9 != ""
|
||||
StrCpy $INSTDIR $R9
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${If} $gDparam != ""
|
||||
StrCpy $INSTDIR $gDparam
|
||||
${EndIf}
|
||||
|
||||
Reference in new issue
Block a user