fix(installer): 真机四条 UI 问题 + 提权改为 highestAvailable
主人真机实测报出四个问题,全部只有在真 Windows 上才暴露(wine 渲染/移动循环/UAC/插件卸载时序都不同): 1. **窗口拖不动**:原来把移动交给 DefWindowProc 的移动循环(ReleaseCapture + WM_NCLBUTTONDOWN HTCAPTION), 我们摘掉了 WS_CAPTION,真机上那个循环不启动。改成自己算位移:按下时记锚点(GetCursorPos + GetWindowRect), 之后每个 tick 用 SetWindowPos 跟光标走。注意起拖后不能因为光标甩出标题栏就停(否则变成「拖一下就断」)。 2. **原生按钮透出**:内层对话框只「盖住」了原生控件,鼠标划过时按钮自己重绘就露出来。现在每页显式隐藏 11 个原生 ID(Next/Cancel/Back + 品牌字 + 页眉),并断言 IsWindowVisible 为假;隐藏后 BM_CLICK 翻页仍有效。 3. **P3 点下一步闪一下没反应**:不是目录校验,是提权失败分支末尾的 Abort(默认选中「为所有用户安装」时 真机必然走到)。现在不再 Abort,降级为按用户安装并弹一句说明后继续。 4. **退出时弹 StdUtils::TimerCreate「Plug-in unloaded before all timers were destroyed」**: 定时器从来没销毁过。加哨兵 $timerOn,在 .onGUIEnd 与 MUI_CUSTOMFUNCTION_ABORT 两条退出路径上幂等销毁; 定时器全程只建一次(翻页不叠加)。对照实验:把销毁段掏空即复现该报错窗口。 **提权策略**(主人拍板「一启动就提权」):RequestExecutionLevel highest。 原先没写这条指令时 NSIS 默认是 requireAdministrator —— 受限账户机器上根本起不来;highest 在管理员机器上 行为与 admin 完全一致(启动即 UAC、全程提权、防火墙必然放行),但给受限账户留了降级路径。 同时去掉 P3 的中途 UAC_RunElevated(那正是「闪一下没反应」的来源)。 验证(wine):五页走完 + 点完成/点 ✕ 退出均无 StdUtils 报错;原生按钮 vis=000;拖动位移精确; /S /currentuser /D=... 与 /S /D=...(非提权时降级按用户)都 exit 0 且写出真卸载器。 UAC 本身与四条观感须真机复验。
This commit is contained in:
1 parent
a1aeed3c55
commit
67eadd7fe6
1 file changed
+132
-56
+132
-56
@@ -131,6 +131,7 @@ Var stBrowse
|
|||||||
Var stFinish
|
Var stFinish
|
||||||
Var uiPage ; 0=无页面,1..5=当前页;定时器靠它判断还能碰哪些控件
|
Var uiPage ; 0=无页面,1..5=当前页;定时器靠它判断还能碰哪些控件
|
||||||
Var timerId
|
Var timerId
|
||||||
|
Var timerOn ; 定时器是否已经创建过(销毁时只认这个哨兵,不靠 id 猜)
|
||||||
Var gDpi ; 系统 DPI(LOGPIXELSX)
|
Var gDpi ; 系统 DPI(LOGPIXELSX)
|
||||||
Var gArt ; 本次要用的素材目录($PLUGINSDIR\1x 或 \2x)
|
Var gArt ; 本次要用的素材目录($PLUGINSDIR\1x 或 \2x)
|
||||||
Var gCx
|
Var gCx
|
||||||
@@ -147,6 +148,10 @@ Var runApp ; 完成页「立即运行」
|
|||||||
Var modeAll ; 安装模式单选:1=为所有用户,0=仅为我
|
Var modeAll ; 安装模式单选:1=为所有用户,0=仅为我
|
||||||
Var instStarted ; 安装流程是否已经跑起来(一次性,见 DoInstall 开头的守卫)
|
Var instStarted ; 安装流程是否已经跑起来(一次性,见 DoInstall 开头的守卫)
|
||||||
Var gIsAdmin ; CheckAdmin 的结果:1 = 当前进程已提权
|
Var gIsAdmin ; CheckAdmin 的结果:1 = 当前进程已提权
|
||||||
|
Var dragCx ; 拖动锚点:按下瞬间的光标屏幕坐标
|
||||||
|
Var dragCy
|
||||||
|
Var dragWx ; 拖动锚点:按下瞬间的窗口左上角
|
||||||
|
Var dragWy
|
||||||
Var gDparam ; 命令行 /D= 指定的安装目录(要在 customInit 的记忆目录之后盖回去)
|
Var gDparam ; 命令行 /D= 指定的安装目录(要在 customInit 的记忆目录之后盖回去)
|
||||||
Var keepShortcuts ; 快捷方式保留策略,由安装流程写定(上游在安装段里声明,我们提前到全局)
|
Var keepShortcuts ; 快捷方式保留策略,由安装流程写定(上游在安装段里声明,我们提前到全局)
|
||||||
Var dragging
|
Var dragging
|
||||||
@@ -242,6 +247,14 @@ Var dragging
|
|||||||
!endif
|
!endif
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
|
; ── 执行级别 ───────────────────────────────────────────────────────────────
|
||||||
|
; highest(= 清单里的 highestAvailable),不是硬 admin:
|
||||||
|
; 管理员账户的机器:一启动就弹 UAC、之后**全程**是提权态 —— 不再有「装到一半才提权」
|
||||||
|
; 那种闪退/卡住,防火墙规则也必然放行得掉;
|
||||||
|
; 受限账户的机器:不弹 UAC,按普通权限启动,走下面的降级路径装成「仅为我安装」,
|
||||||
|
; 至少装得上;硬 admin 在那种机器上直接起不来。
|
||||||
|
RequestExecutionLevel highest
|
||||||
|
|
||||||
; ── 编译期替 electron-builder 补的两处 ──────────────────────────────────────
|
; ── 编译期替 electron-builder 补的两处 ──────────────────────────────────────
|
||||||
; 1) 自定义脚本下 electron-builder 不做 BUILD_UNINSTALLER 那一趟,UNINSTALLER_OUT_FILE
|
; 1) 自定义脚本下 electron-builder 不做 BUILD_UNINSTALLER 那一趟,UNINSTALLER_OUT_FILE
|
||||||
; 被留成空值,而 include/installer.nsh 里有一条无条件的
|
; 被留成空值,而 include/installer.nsh 里有一条无条件的
|
||||||
@@ -256,6 +269,8 @@ Var dragging
|
|||||||
|
|
||||||
; 2) MUI_INSERT 是在第一个 MUI_LANGUAGE 处展开的,界面初始化钩子必须在那之前定义。
|
; 2) MUI_INSERT 是在第一个 MUI_LANGUAGE 处展开的,界面初始化钩子必须在那之前定义。
|
||||||
!define MUI_CUSTOMFUNCTION_GUIINIT "GuiInit"
|
!define MUI_CUSTOMFUNCTION_GUIINIT "GuiInit"
|
||||||
|
; 用户中途取消/关窗时也把定时器收掉(先于 .onGUIEnd 跑,靠 $timerOn 幂等,不会重复销毁)
|
||||||
|
!define MUI_CUSTOMFUNCTION_ABORT "AbortCleanup"
|
||||||
|
|
||||||
; 当前进程是否已提权(结果放 $gIsAdmin)。
|
; 当前进程是否已提权(结果放 $gIsAdmin)。
|
||||||
; 静默安装要靠它决定「为所有用户」还是退一步「仅为我安装」,而这条路上一个 UAC 插件调用都不能有:
|
; 静默安装要靠它决定「为所有用户」还是退一步「仅为我安装」,而这条路上一个 UAC 插件调用都不能有:
|
||||||
@@ -593,6 +608,38 @@ Function HoverBtn
|
|||||||
Push $R7
|
Push $R7
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
|
; 起拖:记下锚点(光标屏幕坐标 + 窗口左上角)
|
||||||
|
Function DragStart
|
||||||
|
Pop $R0
|
||||||
|
StrCpy $dragging 1
|
||||||
|
System::Call '*(i, i) p .R1'
|
||||||
|
System::Call 'user32::GetCursorPos(p R1)'
|
||||||
|
System::Call '*$R1(i .R2, i .R3)'
|
||||||
|
System::Free $R1
|
||||||
|
StrCpy $dragCx $R2
|
||||||
|
StrCpy $dragCy $R3
|
||||||
|
System::Call '*(i, i, i, i) p .R4'
|
||||||
|
System::Call 'user32::GetWindowRect(p $HWNDPARENT, p R4)'
|
||||||
|
System::Call '*$R4(i .R5, i .R6)'
|
||||||
|
System::Free $R4
|
||||||
|
StrCpy $dragWx $R5
|
||||||
|
StrCpy $dragWy $R6
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
; 拖动中:窗口位置 = 锚点窗口位置 + 光标位移
|
||||||
|
Function DragApply
|
||||||
|
Pop $R0
|
||||||
|
System::Call '*(i, i) p .R1'
|
||||||
|
System::Call 'user32::GetCursorPos(p R1)'
|
||||||
|
System::Call '*$R1(i .R2, i .R3)'
|
||||||
|
System::Free $R1
|
||||||
|
IntOp $R2 $R2 - $dragCx
|
||||||
|
IntOp $R3 $R3 - $dragCy
|
||||||
|
IntOp $R2 $R2 + $dragWx
|
||||||
|
IntOp $R3 $R3 + $dragWy
|
||||||
|
System::Call 'user32::SetWindowPos(p $HWNDPARENT, p 0, i $R2, i $R3, i 0, i 0, i 0x0015)'
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
; 定时器回调:光标 → 客户区坐标 → 逐控件做悬停/按下 → 标题栏拖动。
|
; 定时器回调:光标 → 客户区坐标 → 逐控件做悬停/按下 → 标题栏拖动。
|
||||||
; 不做窗口子类化(POC 结论):60ms 轮询足够跟手,也没有子类化回调崩掉整个安装器的风险。
|
; 不做窗口子类化(POC 结论):60ms 轮询足够跟手,也没有子类化回调崩掉整个安装器的风险。
|
||||||
Function Tick
|
Function Tick
|
||||||
@@ -637,34 +684,31 @@ Function Tick
|
|||||||
!insertmacro Track $hFinish $stFinish "btn-finish" ${BTN_PRIMARY_X} ${BTN_Y} ${BTN_W} ${BTN_H}
|
!insertmacro Track $hFinish $stFinish "btn-finish" ${BTN_PRIMARY_X} ${BTN_Y} ${BTN_W} ${BTN_H}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
; 标题栏拖动。用系统拖动循环(HTCAPTION)而不是自己算位移:跟手、贴边、最大化吸附都免费。
|
; 标题栏拖动:按下时记锚点(光标屏幕坐标 + 窗口左上角),之后按位移 SetWindowPos。
|
||||||
; 判定用「屏幕级的左键状态」,所以必须把标题栏按钮那块从拖动区里扣掉,
|
; 不用系统的 WM_NCLBUTTONDOWN / HTCAPTION 移动循环 —— 它把移动交给 DefWindowProc,
|
||||||
; 否则点最小化/关闭会连带开始拖窗(POC 踩过)。
|
; 真机上(窗口已经没有 WS_CAPTION 了)实测拖不动;自己算不依赖系统行为,wine 与真机一致。
|
||||||
${If} $gCy >= 0
|
; 两点关键:
|
||||||
${AndIf} $gCy < ${TB_H}
|
; 1. 命中判定只管「起拖」;已经在拖的时候只要左键还按着就继续跟 —— 光标一旦甩出标题栏
|
||||||
${AndIf} $gCx >= 0
|
; (窗口还没跟上来的那一瞬间)就停,正是「拖一下就断」的经典写法错误;
|
||||||
${AndIf} $gCx < ${TB_MIN_X}
|
; 2. 起拖区里要扣掉标题栏按钮(x >= TB_MIN_X),否则点最小化/关闭会连带开始拖窗。
|
||||||
System::Call 'user32::GetAsyncKeyState(i 1) i .R3'
|
System::Call 'user32::GetAsyncKeyState(i 1) i .R3'
|
||||||
IntOp $R3 $R3 & 0x8000
|
IntOp $R3 $R3 & 0x8000
|
||||||
|
${If} $dragging == 1
|
||||||
${If} $R3 != 0
|
${If} $R3 != 0
|
||||||
${If} $dragging != 1
|
Push true
|
||||||
StrCpy $dragging 1
|
Call DragApply
|
||||||
System::Call '*(i, i) p .R4'
|
|
||||||
System::Call 'user32::GetCursorPos(p R4)'
|
|
||||||
System::Call '*$R4(i .R5, i .R6)'
|
|
||||||
System::Free $R4
|
|
||||||
IntOp $R5 $R5 & 0xFFFF
|
|
||||||
IntOp $R6 $R6 & 0xFFFF
|
|
||||||
IntOp $R6 $R6 << 16
|
|
||||||
IntOp $R5 $R5 | $R6
|
|
||||||
System::Call 'user32::ReleaseCapture()'
|
|
||||||
System::Call 'user32::SendMessageW(p $HWNDPARENT, i ${WM_NCLBUTTONDOWN}, p 2, p $R5)'
|
|
||||||
${EndIf}
|
|
||||||
${Else}
|
${Else}
|
||||||
StrCpy $dragging 0
|
StrCpy $dragging 0
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${Else}
|
${Else}
|
||||||
StrCpy $dragging 0
|
${If} $gCy >= 0
|
||||||
|
${AndIf} $gCy < ${TB_H}
|
||||||
|
${AndIf} $gCx >= 0
|
||||||
|
${AndIf} $gCx < ${TB_MIN_X}
|
||||||
|
${AndIf} $R3 != 0
|
||||||
|
Push true
|
||||||
|
Call DragStart
|
||||||
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
@@ -749,6 +793,14 @@ Function PickDpi
|
|||||||
StrCpy $gDpi $R1
|
StrCpy $gDpi $R1
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
|
; 藏一个外层对话框上的原生控件(控件不存在就跳过,$R7 是本宏专用的临时变量)
|
||||||
|
!macro HideStd ID
|
||||||
|
GetDlgItem $R7 $HWNDPARENT ${ID}
|
||||||
|
${If} $R7 != 0
|
||||||
|
ShowWindow $R7 ${SW_HIDE}
|
||||||
|
${EndIf}
|
||||||
|
!macroend
|
||||||
|
|
||||||
; 内层对话框铺满整个客户区:MUI 外层框架(页眉 / 品牌字 / 原生按钮条)全被盖住,
|
; 内层对话框铺满整个客户区:MUI 外层框架(页眉 / 品牌字 / 原生按钮条)全被盖住,
|
||||||
; 自绘位图才有完整的 760×480 画布。
|
; 自绘位图才有完整的 760×480 画布。
|
||||||
Function NewPage
|
Function NewPage
|
||||||
@@ -758,8 +810,11 @@ Function NewPage
|
|||||||
Abort
|
Abort
|
||||||
${EndIf}
|
${EndIf}
|
||||||
; 悬停/拖动全靠这个 60ms 轮询(POC 结论:比窗口子类化稳,代价是光标移动要等一个 tick)。
|
; 悬停/拖动全靠这个 60ms 轮询(POC 结论:比窗口子类化稳,代价是光标移动要等一个 tick)。
|
||||||
; 建在这里而不是 .onGUIInit:StdUtils 的定时器要挂在页对话框上才跑得起来。
|
; 建在页面里而不是 .onGUIInit:StdUtils 的定时器要挂在页对话框上才跑得起来。
|
||||||
${If} $timerId == 0
|
; ⚠️ 全程只建一个:它挂在 StdUtils 的隐藏窗口上,不随页面消失,而 NewPage 每页都会跑 ——
|
||||||
|
; 不加哨兵就会越翻页越多(越跑越卡,退出时还会漏掉没销毁的那些)。
|
||||||
|
${If} $timerOn == 0
|
||||||
|
StrCpy $timerOn 1
|
||||||
${StdUtils.TimerCreate} $timerId Tick 60
|
${StdUtils.TimerCreate} $timerId Tick 60
|
||||||
${EndIf}
|
${EndIf}
|
||||||
Push ${WIN_W}
|
Push ${WIN_W}
|
||||||
@@ -769,6 +824,22 @@ Function NewPage
|
|||||||
Call S
|
Call S
|
||||||
Pop $R1
|
Pop $R1
|
||||||
System::Call 'user32::SetWindowPos(p $hPage, p 0, i 0, i 0, i $R0, i $R1, i 0x0004)'
|
System::Call 'user32::SetWindowPos(p $hPage, p 0, i 0, i 0, i $R0, i $R1, i 0x0004)'
|
||||||
|
; 把外层对话框上的原生控件**显式隐藏**。内层对话框只是盖在它们上面,鼠标划过那块区域时
|
||||||
|
; 原生按钮会自己重绘、从自绘层底下透出来(真机实测:Next/Back/Cancel 会闪出来)。
|
||||||
|
; NSIS 每页都重建内层对话框,所以每页(=每次 NewPage)都要重新藏一遍。
|
||||||
|
; ⚠️ 藏掉之后翻页仍然靠 SendMessage BM_CLICK 点那个被藏起来的 Next —— 隐藏窗口照样收得到
|
||||||
|
; 消息,本机实测翻页正常(这条一旦不成立,整套翻页就断了,所以改了要立刻验证)。
|
||||||
|
!insertmacro HideStd 1 ; Next
|
||||||
|
!insertmacro HideStd 2 ; Cancel
|
||||||
|
!insertmacro HideStd 3 ; Back
|
||||||
|
!insertmacro HideStd 1028 ; 底部品牌字底
|
||||||
|
!insertmacro HideStd 1256 ; 底部品牌字
|
||||||
|
!insertmacro HideStd 1034 ; 页眉底
|
||||||
|
!insertmacro HideStd 1035 ; 分隔线(标准)
|
||||||
|
!insertmacro HideStd 1045 ; 分隔线(满窗)
|
||||||
|
!insertmacro HideStd 1037 ; 页眉标题
|
||||||
|
!insertmacro HideStd 1038 ; 页眉副标题
|
||||||
|
!insertmacro HideStd 1046 ; 页眉图标
|
||||||
; 上一页的句柄全部作废:定时器靠这组变量判断「还能碰谁」
|
; 上一页的句柄全部作废:定时器靠这组变量判断「还能碰谁」
|
||||||
StrCpy $hBg 0
|
StrCpy $hBg 0
|
||||||
StrCpy $hBrand 0
|
StrCpy $hBrand 0
|
||||||
@@ -1154,37 +1225,24 @@ Function PageDirLeave
|
|||||||
StrCpy $INSTDIR "$INSTDIR\${APP_FILENAME}"
|
StrCpy $INSTDIR "$INSTDIR\${APP_FILENAME}"
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
; 兜底:万一路径是空的(记忆为空 + 默认值也没算出来),给一个合法的按用户目录,
|
||||||
|
; 绝不把空 $INSTDIR 带进安装流程(空路径是「点了没反应 / 装到莫名其妙的地方」的经典来源)。
|
||||||
|
${If} $INSTDIR == ""
|
||||||
|
StrCpy $INSTDIR "$LOCALAPPDATA\Programs\${APP_FILENAME}"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
${If} $modeAll == 1
|
${If} $modeAll == 1
|
||||||
; 「为所有用户安装」:照抄 multiUserUi.nsh 的提权范式。
|
; 「为所有用户安装」。执行级别是 highest,所以**管理员账户的机器上启动时就提过权了**,
|
||||||
; 提权成功后是**另一个进程**在跑整个向导(它已经是管理员,会重新走到这一页并直接接着往下),
|
; 这里不需要(也不该)再走一次 UAC_RunElevated:中途提权会把窗口藏起来、让用户看见
|
||||||
; 外层进程在 ${Case} 0 里 Quit,避免两个进程同时写同一份文件。
|
; 「闪一下没反应」,真机上就是这么坏的(前几轮修过)。
|
||||||
${IfNot} ${UAC_IsAdmin}
|
; 受限账户的机器上 highest 拿不到管理员权限 —— 这时明确告知并降级成「仅为我安装」继续,
|
||||||
ShowWindow $HWNDPARENT ${SW_HIDE}
|
; 绝不停在这一页。
|
||||||
!insertmacro UAC_RunElevated
|
${If} ${UAC_IsAdmin}
|
||||||
${Switch} $0
|
|
||||||
${Case} 0
|
|
||||||
${If} $1 = 1
|
|
||||||
Quit
|
|
||||||
${EndIf}
|
|
||||||
${If} $1 = 3
|
|
||||||
${OrIf} $2 = 0x666666
|
|
||||||
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "$(loginWithAdminAccount)"
|
|
||||||
${EndIf}
|
|
||||||
${Break}
|
|
||||||
${Case} 1223 ;用户取消了 UAC
|
|
||||||
${Break}
|
|
||||||
${Case} 1062
|
|
||||||
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "登录服务未运行,无法提权。"
|
|
||||||
${Break}
|
|
||||||
${Default}
|
|
||||||
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "提权失败,错误码 $0"
|
|
||||||
${Break}
|
|
||||||
${EndSwitch}
|
|
||||||
ShowWindow $HWNDPARENT ${SW_SHOW}
|
|
||||||
BringToFront
|
|
||||||
Abort ; 留在本页:提权没成时让用户改选「仅为我安装」
|
|
||||||
${Else}
|
|
||||||
!insertmacro setInstallModePerAllUsers
|
!insertmacro setInstallModePerAllUsers
|
||||||
|
${Else}
|
||||||
|
MessageBox mb_IconExclamation|mb_TopMost|mb_SetForeground "当前账户没有管理员权限,无法「为所有用户安装」,已改为「仅为我安装」继续。"
|
||||||
|
StrCpy $modeAll 0
|
||||||
|
!insertmacro setInstallModePerUser
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${Else}
|
${Else}
|
||||||
!insertmacro setInstallModePerUser
|
!insertmacro setInstallModePerUser
|
||||||
@@ -1279,10 +1337,28 @@ FunctionEnd
|
|||||||
; ── .onInit ────────────────────────────────────────────────────────────────
|
; ── .onInit ────────────────────────────────────────────────────────────────
|
||||||
; 逐项对照 electron-builder 的 templates/nsis/installer.nsi,只去掉 setInstallSectionSpaceRequired
|
; 逐项对照 electron-builder 的 templates/nsis/installer.nsi,只去掉 setInstallSectionSpaceRequired
|
||||||
; (那是给组件页算体积用的,我们没有组件页)。
|
; (那是给组件页算体积用的,我们没有组件页)。
|
||||||
|
; 安装器 GUI 结束时收尾(正常点「完成」退出、以及中途关窗都走这里)。
|
||||||
|
; 不销毁的话 StdUtils 插件在卸载时会弹「Plug-in unloaded before all timers were destroyed」。
|
||||||
|
; 只认哨兵 $timerOn:对没建过 / 已销毁的 id 调 TimerDestroy 本身也是一种报错源。
|
||||||
|
Function AbortCleanup
|
||||||
|
${If} $timerOn == 1
|
||||||
|
StrCpy $timerOn 0
|
||||||
|
${StdUtils.TimerDestroy} $R0 $timerId
|
||||||
|
StrCpy $timerId 0
|
||||||
|
${EndIf}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function .onGUIEnd
|
||||||
|
${If} $timerOn == 1
|
||||||
|
StrCpy $timerOn 0
|
||||||
|
${StdUtils.TimerDestroy} $R0 $timerId
|
||||||
|
StrCpy $timerId 0
|
||||||
|
${EndIf}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
Function .onInit
|
Function .onInit
|
||||||
; 显式赋 "0":下面 NewPage 里用 $timerId == 0 判断「建过没有」,
|
|
||||||
; 而 LogicLib 的 == 是字符串比较,空串跟 0 不相等,会一次都不建(踩过)
|
|
||||||
StrCpy $timerId 0
|
StrCpy $timerId 0
|
||||||
|
StrCpy $timerOn 0
|
||||||
StrCpy $uiPage 0
|
StrCpy $uiPage 0
|
||||||
StrCpy $instStarted 0
|
StrCpy $instStarted 0
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
|
|||||||
Reference in new issue
Block a user