参考· 04
应用标识与覆盖安装
这是打包里最容易踩坑、又最不容易发现的一处:AppId 决定重新打包是覆盖升级,还是装出第二个。
Inno 怎么判断「升级」还是「新装」
Inno 靠 AppId 认出一个应用之前装过没有。它把记录写在
HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\<AppId>_is1:
- AppId 相同 → 视为同一个应用的新版本:覆盖安装、更新卸载键里的版本号、
向导默认装到上次的目录(
UsePreviousAppDir默认yes); - AppId 不同 → 视为另一个应用:全新安装,两个版本在「应用和功能」里并存。
所以 AppId 必须在「同一个产品的所有构建」之间保持一致。
为什么不能用工程 ID
工程 ID 是「新建工程」时随机生成的 UUID。用它当 AppId 的话:
- 同一个工程反复打包 → AppId 不变 → 能覆盖升级;
- 但换一个工程(哪怕选的是同一份 exe)→ AppId 变了 → 全新安装、两个版本并存。
这正是「我明明只改了几行代码重新打包,装出来却是两个软件」的原因。
现在是怎么做的
AppId 由主程序的产品身份推导:版本资源里的 ProductName + CompanyName +
OriginalFilename(拿不到就退到文件名),拼接后做一次确定性哈希,摆成 UUIDv5 的形状。
- 同一份主程序重新编译、重新打包、换台机器打包 → 同一个 AppId;
ProductName或CompanyName改了 → AppId 跟着变 —— 那本来就该当成另一个应用;- 推导结果填在「应用标识」里,可以手工改(比如要强行当成另一个应用,或要接回旧的 AppId)。
推导逻辑(commands.rs::detect_app_id)有单测钉住:确定性、不同产品得到不同值、
UUID 形状合法、只改大小写不算换产品、字段之间不会串味。
两个容易忽略的前提
PrivilegesRequired不能在两次构建之间变。 它决定 Inno 把卸载键写在 HKCU 还是 HKLM; 上一次是「当前用户」、这一次改成「需要管理员」,第二次就找不到第一次的安装 → 全新安装。- 绿色版(
packagingMode=portable)没有升级这件事。 它写Uninstallable=no与CreateUninstallRegKey=no,Inno 没有可查的记录,每次都是全新解压。这是有意的。
版本迁移的注意点
AppId 的取值方式在 v0.1.1 改过一次(v0.1.0 用的是工程 ID)。因此:
「v0.1.0 构建的安装包」与「v0.1.1 之后构建的安装包」AppId 不同,不会互相升级 —— 只会发生一次。想接回旧 AppId,在「应用标识」里手工填入旧值即可。
怎么确认自己的包是对的
- 打包两次(或改个版本号再打一次),分别安装;
- 打开「设置 → 应用 → 已安装的应用」,确认只有一条记录;
- 或者直接看注册表:
HKCU\...\Uninstall\下同一个_is1键的DisplayVersion变了、键数没变。