21 KiB
cargo-wix 打包与 WiX 常用修改指南
本文档说明本项目如何使用 cargo-wix 生成 Windows MSI 安装包,以及常见 WiX 配置的修改方法。当前项目的安装配置文件是 wix/main.wxs,Rust 包信息来自 Cargo.toml。
1. 当前项目打包方式
本项目使用:
- Rust/Cargo 构建 release 可执行文件
cargo-wix读取Cargo.toml和wix/main.wxs- WiX Toolset v3 编译并链接生成
.msi
当前可执行文件配置在 Cargo.toml:
[[bin]]
name = "ESkinPlayer"
path = "src/main.rs"
当前 WiX 元信息:
[package.metadata.wix]
eula = false
当前 MSI 默认生成到:
target\wix\eskin-model-player-<version>-x86_64.msi
例如:
target\wix\eskin-model-player-5.0.0-x86_64.msi
2. 环境准备
需要安装:
- Rust toolchain
- WiX Toolset v3
- cargo-wix
检查 Rust:
rustc --version
cargo --version
安装 cargo-wix:
cargo install cargo-wix
检查 cargo-wix:
cargo wix --help
本机常见 WiX v3 路径:
C:\Program Files (x86)\WiX Toolset v3.14\bin
检查 WiX 工具是否存在:
Test-Path "C:\Program Files (x86)\WiX Toolset v3.14\bin\candle.exe"
Test-Path "C:\Program Files (x86)\WiX Toolset v3.14\bin\light.exe"
3. 常用打包命令
进入项目目录:
cd D:\eskin-player
清理旧的 WiX 输出:
cargo wix clean
正常打包:
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --nocapture
说明:
--bin-path指定 WiX Toolset 的bin目录。--nocapture显示candle.exe和light.exe的详细输出,排查错误时建议加上。- 默认会先执行
cargo build --release。 - 默认输出到
target\wix。
查看生成的 MSI:
Get-ChildItem target\wix\*.msi
指定输出目录:
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --output dist\
指定输出文件名:
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --output dist\ESkinPlayer.msi
只打包,不重新构建 Rust:
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --no-build --target-bin-dir target\release
打包后立即安装:
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --install
4. 重要文件说明
Cargo.toml
Cargo.toml 决定包名、版本、描述、作者、二进制名称等。
常见字段:
[package]
name = "eskin-model-player"
version = "5.0.0"
authors = ["JOYSONQUIN"]
description = "Desktop pressure sensor visualization and playback application."
这些字段会影响:
- MSI 文件名
- 控制面板里的产品版本
- WiX 模板变量
修改版本号后重新打包:
version = "5.0.1"
Windows Installer 的升级判断依赖版本号。发布新的安装包时建议递增版本号。
wix/main.wxs
wix/main.wxs 是 WiX 主配置文件。它决定:
- 安装包名称
- 安装范围
- 安装目录
- 包含哪些文件
- 是否写 PATH
- 卸载行为
- UI 样式
- 升级规则
- 图标、EULA、快捷方式等
5. Product 和 Package
典型结构:
<Product
Id='*'
Name='eskin-model-player'
UpgradeCode='7CEF316B-BE23-4533-B4B2-87D06D2230B5'
Manufacturer='JOYSONQUIN'
Language='1033'
Codepage='1252'
Version='$(var.Version)'>
<Package Id='*'
Keywords='Installer'
Description='Desktop pressure sensor visualization and playback application.'
Manufacturer='JOYSONQUIN'
InstallerVersion='450'
Languages='1033'
Compressed='yes'
InstallScope='perUser'
InstallPrivileges='limited'
SummaryCodepage='1252'/>
</Product>
关键字段:
Product/@Id='*':每次构建生成新的 ProductCode。UpgradeCode:同一个产品线必须保持稳定,不要随意修改。Version='$(var.Version)':使用Cargo.toml的版本。InstallScope:安装范围,常见值是perUser或perMachine。InstallPrivileges:权限要求,limited表示不要求管理员权限,elevated表示需要提升权限。
6. per-user 和 per-machine 的区别
当前用户安装:perUser
适合:
- 不希望安装时弹管理员权限
- 应用只给当前用户使用
- 安装到用户目录,例如
%LOCALAPPDATA% - 写用户 PATH 或 HKCU 注册表
推荐配置:
<Package
...
InstallScope='perUser'
InstallPrivileges='limited'/>
目录使用:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
环境变量使用用户级 PATH:
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='no'/>
注意:
- 安装到用户目录的组件需要使用
HKCU注册表项作为KeyPath。 - 用户目录下的安装目录建议添加
RemoveFolder,否则 ICE64 会报错。
所有用户安装:perMachine
适合:
- 应用需要给所有用户使用
- 安装到
Program Files - 写系统 PATH
- 写 HKLM 注册表
- 公司 IT 管理员部署
推荐配置:
<Package
...
InstallScope='perMachine'
InstallPrivileges='elevated'/>
目录使用:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
64 位安装包通常使用:
<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
然后:
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
系统 PATH:
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='yes'/>
注意:
perMachine通常需要管理员权限。- 如果普通用户安装,会出现权限不足提示。
- 写
Program Files和系统 PATH 都需要提升权限。
7. 当前项目的 per-user 配置要点
当前项目已经改为当前用户安装:
InstallScope='perUser'
InstallPrivileges='limited'
安装目录:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
<Directory Id='Bin' Name='bin'>
...
</Directory>
</Directory>
</Directory>
</Directory>
PATH 组件使用 HKCU registry key 作为 KeyPath:
<Component Id='Path' Guid='FD230ABD-75B9-47D4-870B-CF2EA72B76BE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='PathComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<RemoveFolder Id='RemoveBinFolder' Directory='Bin' On='uninstall'/>
<RemoveFolder Id='RemoveApplicationFolder' Directory='APPLICATIONFOLDER' On='uninstall'/>
<Environment
Id='PATH'
Name='PATH'
Value='[Bin]'
Permanent='no'
Part='last'
Action='set'
System='no'/>
</Component>
程序文件组件也使用 HKCU registry key 作为 KeyPath:
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='BinaryComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='exe0'
Name='ESkinPlayer.exe'
DiskId='1'
Source='$(var.CargoTargetBinDir)\ESkinPlayer.exe'/>
</Component>
8. Component、File、KeyPath 的基本规则
WiX 的核心概念:
Directory:安装目录。Component:安装和卸载的最小管理单元。File:要安装的文件。RegistryValue:要写入的注册表项。KeyPath:Windows Installer 用来判断组件是否已安装的关键路径。
常见规则:
- 一个组件通常只放一个主要文件。
- 组件 GUID 一旦发布后不要随意修改。
perUser安装到用户目录时,建议用HKCUregistry key 作为KeyPath。perMachine安装到 Program Files 时,文件通常可以作为KeyPath。- 如果组件同时包含文件和注册表 KeyPath,WiX v3 通常不能用
Guid='*'自动生成 GUID,需要写固定 GUID。
生成 GUID:
[guid]::NewGuid().ToString().ToUpper()
示例:
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
...
</Component>
9. 添加文件
假设要安装一个 README.txt 到应用目录。
目录结构:
wix\
main.wxs
README.txt
添加组件:
<Component Id='ReadmeFile' Guid='PUT-GUID-HERE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='ReadmeComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='ReadmeTxt'
Name='README.txt'
DiskId='1'
Source='wix\README.txt'/>
</Component>
然后在 Feature 里引用:
<ComponentRef Id='ReadmeFile'/>
如果是 perMachine 安装,也可以简化为:
<Component Id='ReadmeFile' Guid='PUT-GUID-HERE'>
<File
Id='ReadmeTxt'
Name='README.txt'
DiskId='1'
Source='wix\README.txt'
KeyPath='yes'/>
</Component>
10. 添加资源目录
如果程序运行时需要模型、图片、配置文件等资源,建议明确安装到应用目录下。
示例:
<Directory Id='AssetsFolder' Name='assets'>
<Component Id='AssetConfig' Guid='PUT-GUID-HERE'>
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='AssetConfigComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
<File
Id='AssetConfigJson'
Name='config.json'
DiskId='1'
Source='assets\config.json'/>
</Component>
</Directory>
引用:
<ComponentRef Id='AssetConfig'/>
如果资源很多,不建议长期手写大量 <File>。可以考虑:
- 用 WiX Heat 自动收集目录
- 在构建脚本里生成
.wxs片段 - 只把稳定的少量资源手写到
main.wxs
11. 修改安装目录
改为当前用户目录
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
实际路径通常类似:
C:\Users\<user>\AppData\Local\eskin-model-player
改为 Program Files
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
64 位项目推荐:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFiles64Folder'>
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
...
</Directory>
</Directory>
</Directory>
12. 是否添加 PATH
当前项目有一个可选 Feature:把安装目录下的 bin 加入 PATH。
Feature:
<Feature
Id='Environment'
Title='PATH Environment Variable'
Description='Add the install location of the [ProductName] executable to the PATH system environment variable.'
Level='1'
Absent='allow'>
<ComponentRef Id='Path'/>
</Feature>
如果不希望修改 PATH,可以删除或注释:
<ComponentRef Id='Path'/>
也可以把整个 Environment Feature 注释掉。
用户级 PATH:
System='no'
系统级 PATH:
System='yes'
注意:
- 用户级 PATH 不需要管理员权限。
- 系统级 PATH 需要管理员权限。
- 安装后新开的终端才能看到新的 PATH。
- 已打开的 PowerShell/CMD 通常不会自动刷新环境变量。
13. 添加开始菜单快捷方式
常见做法是在 ProgramMenuFolder 下创建快捷方式。
目录:
<Directory Id='ProgramMenuFolder'>
<Directory Id='ApplicationProgramsFolder' Name='eskin-model-player'/>
</Directory>
在可执行文件组件里添加:
<Shortcut
Id='ApplicationStartMenuShortcut'
Directory='ApplicationProgramsFolder'
Name='ESkinPlayer'
WorkingDirectory='Bin'
Advertise='no'
Target='[Bin]ESkinPlayer.exe'/>
<RemoveFolder Id='RemoveApplicationProgramsFolder' Directory='ApplicationProgramsFolder' On='uninstall'/>
如果是 per-user 安装,快捷方式组件也应该使用 HKCU registry key 作为 KeyPath。
14. 添加桌面快捷方式
目录:
<Directory Id='DesktopFolder' Name='Desktop'/>
快捷方式:
<Shortcut
Id='ApplicationDesktopShortcut'
Directory='DesktopFolder'
Name='ESkinPlayer'
WorkingDirectory='Bin'
Advertise='no'
Target='[Bin]ESkinPlayer.exe'/>
桌面快捷方式不一定适合默认创建。更推荐放到可选 Feature,或者只创建开始菜单快捷方式。
15. 修改控制面板图标
准备 .ico 文件,例如:
wix\Product.ico
启用:
<Icon Id='ProductICO' SourceFile='wix\Product.ico'/>
<Property Id='ARPPRODUCTICON' Value='ProductICO'/>
注意:
.ico最好包含多个尺寸,例如 16、32、48、256。- 路径相对执行打包时的项目根目录。
16. 添加 EULA
如果需要安装时显示许可协议:
- 准备 RTF 文件,例如
wix\Eula.rtf - 修改
Cargo.toml
[package.metadata.wix]
eula = true
- 在
wix/main.wxs中启用:
<WixVariable Id='WixUILicenseRtf' Value='wix\Eula.rtf'/>
如果当前使用 WixUI_FeatureTree 并跳过 EULA,需要检查 <UI> 里的 Publish 逻辑,避免安装流程跳转冲突。
17. 修改安装 UI
当前项目使用:
<UI>
<UIRef Id='WixUI_FeatureTree'/>
...
</UI>
常见 UI:
WixUI_Minimal:最简单。WixUI_InstallDir:允许选择安装目录。WixUI_FeatureTree:允许选择 Feature。WixUI_Advanced:更完整的高级安装流程。
如果要允许用户改安装路径,常用:
<UIRef Id='WixUI_InstallDir'/>
<Property Id='WIXUI_INSTALLDIR' Value='APPLICATIONFOLDER'/>
18. 升级策略
当前项目使用:
<MajorUpgrade
Schedule='afterInstallInitialize'
DowngradeErrorMessage='A newer version of [ProductName] is already installed. Setup will now exit.'/>
含义:
- 安装新版本时自动卸载旧版本。
- 不允许安装更旧版本覆盖新版本。
UpgradeCode必须保持不变。Version必须递增。
Windows Installer 版本规则:
- 通常只比较前三段版本号:
Major.Minor.Build 5.0.0到5.0.1是升级- 只重打同一个
5.0.0可能不会触发正常升级
发布新版时建议:
version = "5.0.1"
19. 签名 MSI
未签名 MSI 在 Windows 上可能触发安全提示。正式发布建议签名。
cargo-wix 支持 sign 子命令,也可以在生成 MSI 后用 signtool.exe。
示例:
signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a target\wix\eskin-model-player-5.0.0-x86_64.msi
实际签名命令取决于证书来源:
- 本机证书
- PFX 文件
- 硬件 token
- 公司代码签名服务
20. 常见错误与处理
权限不足
错误示例:
You do not have sufficient privileges to complete this installation for all users of the machine.
常见原因:
InstallScope='perMachine'- 安装到
ProgramFilesFolder - 写系统 PATH:
System='yes' - 写 HKLM 注册表
解决:
- 以管理员身份运行 MSI
- 或改成
perUser - 或安装到
LocalAppDataFolder - 或把 PATH 改为
System='no'
ICE38
错误示例:
ICE38: Component ... installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
原因:
- 组件安装到用户目录
- 但组件使用文件作为
KeyPath
解决:
<RegistryValue
Root='HKCU'
Key='Software\JOYSONQUIN\eskin-model-player'
Name='BinaryComponent'
Type='integer'
Value='1'
KeyPath='yes'/>
并移除 File 上的:
KeyPath='yes'
ICE64
错误示例:
ICE64: The directory Bin is in the user profile but is not listed in the RemoveFile table.
原因:
- 用户目录下创建了目录
- 卸载时没有明确清理
解决:
<RemoveFolder Id='RemoveBinFolder' Directory='Bin' On='uninstall'/>
<RemoveFolder Id='RemoveApplicationFolder' Directory='APPLICATIONFOLDER' On='uninstall'/>
CNDL0230
错误示例:
The Component/@Guid attribute's value '*' is not valid for this component because it does not meet the criteria for having an automatically generated guid.
原因:
- 组件里有文件
- 但 KeyPath 是 registry value
- WiX v3 不能自动生成这种组件 GUID
解决:
生成固定 GUID:
[guid]::NewGuid().ToString().ToUpper()
然后写入:
<Component Id='binary0' Guid='302CAFB5-6951-426B-BC5A-988C351A2CF2'>
ICE91
警告示例:
ICE91: The file 'exe0' will be installed to the per user directory ...
原因:
- 文件安装到用户目录
- ICE 提醒如果该包被当作 per-machine 安装,文件不会复制到每个用户的 profile
处理:
- 如果安装包明确是
perUser,通常可以忽略。 - 如果目标是
perMachine,应改用ProgramFilesFolder,不要安装到用户 profile。
新 MSI 没有覆盖旧 MSI
常见原因:
Cargo.toml版本号没变UpgradeCode改了- 旧版本是 per-machine,新版本是 per-user
解决:
- 发布时递增版本号
- 保持
UpgradeCode不变 - 如果旧版本是 per-machine,先用管理员权限卸载旧版本
21. 调试 MSI 安装
生成详细安装日志:
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v install.log
静默安装:
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /qn /l*v install.log
卸载:
msiexec /x target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v uninstall.log
如果安装失败,优先查看日志里:
Return value 3
它附近通常是真正失败原因。
22. 推荐发布流程
- 更新
Cargo.toml版本号。 - 确认
wix/main.wxs的安装范围符合发布目标。 - 执行 release 构建和 MSI 打包。
- 在干净机器或干净用户环境中测试安装。
- 测试启动程序。
- 测试卸载后文件、快捷方式、PATH 是否清理干净。
- 正式发布前对 MSI 签名。
命令:
cd D:\eskin-player
cargo wix clean
cargo wix --bin-path "C:\Program Files (x86)\WiX Toolset v3.14\bin" --nocapture
验证:
Get-ChildItem target\wix\*.msi
msiexec /i target\wix\eskin-model-player-5.0.0-x86_64.msi /l*v install.log
23. 本项目常用修改清单
改包名
修改 Product/@Name:
Name='eskin-model-player'
以及 Cargo.toml:
name = "eskin-model-player"
改显示厂商
修改:
Manufacturer='JOYSONQUIN'
以及:
authors = ["JOYSONQUIN"]
改安装目录名
修改:
<Directory Id='APPLICATIONFOLDER' Name='eskin-model-player'>
改可执行文件名
修改 Cargo.toml:
[[bin]]
name = "ESkinPlayer"
同步修改 WiX:
<File
Id='exe0'
Name='ESkinPlayer.exe'
Source='$(var.CargoTargetBinDir)\ESkinPlayer.exe'/>
关闭 PATH 写入
删除或注释:
<ComponentRef Id='Path'/>
也可以删除整个 Environment Feature。
从 per-user 改回 per-machine
修改:
InstallScope='perMachine'
InstallPrivileges='elevated'
安装目录改为:
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
PATH 改为:
System='yes'
注意:
- 这会重新要求管理员权限。
- 如果已经发布过 per-user 版本,切换安装范围前需要测试升级和卸载路径。
24. 维护注意事项
UpgradeCode是产品线标识,正常发布不要改。- 发布新版本时递增
Cargo.toml的version。 - 已发布组件的
Guid不要随意变更。 - 用户目录安装使用
HKCUregistry key 作为KeyPath。 - 系统目录安装通常需要管理员权限。
- 修改 PATH 后要测试安装和卸载。
- 加文件后必须在
Feature中添加ComponentRef,否则不会被安装。 - WiX 报错时优先看
candle还是light阶段:candle:XML 结构、语法、GUID、路径等编译错误。light:链接、ICE 校验、MSI 表规则问题。