ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

wix311-binaries.zip实战:从XML到MSI的WiX构建流程与避坑指南

wix311-binaries.zip实战:从XML到MSI的WiX构建流程与避坑指南 简介WiX 3.11 版二进制资源包面向需要构建 Windows 安装程序的开发与运维人员用 XML 描述安装流程可生成 MSI 包并实现标准化打包适合从手动打包转向自动化发布的团队解决手工制作安装程序繁琐且难以复用的问题。压缩包约 33MB内含多个配套配置文件对应编译器、链接器、资源收集与元数据提取等核心工具可用于调整命令行参数、日志级别、默认输出目录及特定转换规则这些配置文件通常以 XML 结构保存便于版本化管理与团队共享。已有 465 人浏览学习适合初中级使用者理解 WiX 工具链协作方式也可作为企业安装包团队的参考模板。通过对比这些配置能快速掌握各工具在打包任务中的分工构建失败或链接出错时还能从配置层面定位原因提升安装包制作效率。1. 为什么还在用 wix311-binaries.zip先认清这个包里装的是什么如果你接手过一个有点年份的安装包工程大概率在 CI 脚本里见过这样一段解压 wix311-binaries.zip调用 candle.exe调用 light.exe最后产出一个 .msi。这个 zip 是 Windows Installer XMLWiX3.11 版本的官方二进制发行包里面没有安装向导、没有图形界面只有一组命令行工具解压即用。它解决的核心问题很简单把用 XML 描述的安装逻辑编译成标准 MSI 安装包全程不依赖 Visual Studio适合在 Jenkins 这类构建环境里无人值守地跑。适合谁维护存量 WiX 工程的人、需要在批处理里出包的人、以及不想为一个安装包需求引入整套 VS 工程的人。下面直接进入工具清单和构建流程把参数、边界和踩过的坑一次说清。2. 工具盘点和两段式编译wix311-binaries.zip 里的可执行文件2.1 从 wxs 到 wixobj 再到 msicandle 与 light 的分工WiX 的构建是一条两段式流水线。源文件是 .wxsXML 格式candle.exe 负责把它编译成 .wixobj 中间文件light.exe 再把中间文件链接成最终 .msi。这和我们写 C 代码出 exe 的逻辑一样也分“编译”和“链接”两步。之所以拆两步不是因为 WiX 故弄玄虚而是中间产物可以缓存复用当你只改了版本号或者某个文件路径时重跑 light 比全量编译快得多。另一个理由是light 阶段会跑一组叫 ICEInternal Consistency Evaluators的一致性检查把安装包层面的规则问题暴露在构建期而不是等用户装到一半才报错——相当于链接器顺手帮你查了一遍“内存越界”。wixobj 对大多数人来说是个黑匣子不需要打开它但你要知道它存在。candle 的常用参数我从实际使用里挑几个列出参数作用示例-d定义预处理变量wxs 里用 $(var.xxx) 引用-dBuildDirrelease\bin-o指定输出 wixobj 路径-o build\MyApp.wixobj-arch指定目标架构 x86/x64影响目录变量解析-arch x64-ext加载 WiX 扩展程序集-ext WixUtilExtension-sw压制指定编号的警告-sw1002一个典型的编译命令长这样candle.exe -nologo -arch x64 -dBuildDirrelease\bin -out build\MyApp.wixobj MyApp.wxs命令里的 -nologo 是去掉版本横幅让 CI 日志干净一些。-arch x64 是让 WiX 按 64 位产品来解析标准目录变量这个参数漏掉是后面踩坑章节第一名的直接原因。如果你暂时不确定架构至少要知道有这个开关存在不要在 32 位和 64 位之间靠猜。2.2 不止 candle 和 lightheat、torch、pyro 与 insignia 的职责除了核心的编译链接工具bin 目录下还躺着几个平时不怎么碰、但特定场景必须用的工具。heat.exe 是“目录收割器”。常见做法是编译产物是一整个 web 静态资源目录里面可能上百个文件手写 wxs 把这些文件一个个列出来既不现实也没必要。heat 直接扫描目录自动生成一个包含所有文件的 ComponentGroup 片段。典型命令heat.exe dir dist -cg WebFiles -gg -g1 -srd -sf -var var.DistDir -out dist.wxs参数含义dir 是子命令后面跟要扫描的目录。-cg 指定生成的组件组名称供 Feature 里引用。-gg 让 heat 为每个组件自动生成 GUID-g1 表示生成的 GUID 不带花括号。-srd 表示不生成 SourceDir 根引用这样最终文件路径可以完全交给变量控制。-sf 的意思是不要为每个文件拆出独立 fragment全部塞进一个组件组里方便引用。-var var.DistDir 是这里最关键的一个heat 会把文件源路径替换成 $(var.DistDir)... 这样的变量引用真正路径由 candle 的 -dDistDir 参数在编译时决定。这样 wxs 里就不会出现绝对路径换台机器构建不用改文件。其他工具的使用频率更低工具职责什么时候碰candle.exewxs 编译为 wixobj每次构建light.exewixobj 链接为 msi每次构建heat.exe目录/项目生成 wxs 片段新增成批文件时torch.exe合并两个 wixout、生成补丁源做升级补丁时pyro.exe把补丁源打包成 msp打补丁包时insignia.exe引导程序数字签名收尾做自定义 burn bundle 时vit.exe对照两个 msi 数据库差异排查安装结果差异时日常出包其实只用到前三个。torch 和 pyro 是“升级补丁”这条线的工具属于另一个独立工作流不在常规 MSI 构建里出现。第一次用 WiX 的人不需要把它们全部搞懂但至少知道工具箱里有这些遇到对应需求时能想起名字就够。2.3 为什么 3.11 在 WiX 4 出来之后仍是默认选择这是新人最容易困惑的问题明明 WiX 4 都出了为什么网上大量存量工程和 CI 脚本还在用 wix311。核心原因有三个。第一WiX 4 的底层实现换过wixlib 格式、扩展机制、burn bundle 的结构都有变化老工程迁移不是改个版本号那么简单而要过一遍构建脚本和自定义扩展对“安装包能稳定产出”这个目标来说迁移收益不明显成本却摆在眼前。第二WiX 3.x 时代积累的资料量最大遇到问题搜出来的答案大部分还是 3.x 语法照着 3.11 写不会卡壳。第三3.11 是 3.x 系列最后一个大版本该修的兼容性问题修得相对干净属于这个系列里最稳定的一代。这不代表 3.11 完美。新项目我一般会直接看 WiX 4但接手老工程或者要快速在 CI 里把 MSI 出出来把 3.11 吃透更实际。工具选型这件事上“存量兼容”往往压过“技术先进”这句话在安装包领域尤其成立。3. 从解压到一条命令出 MSI最小 wxs、candle 与 light 的构建流程3.1 解压到纯英文路径把 bin 加进 PATHwix311-binaries.zip 不需要安装解压即用。但有一个硬性习惯解压路径不要有空格、不要有中文。推荐直接解压到 C:\wix311 这种纯英文短路径。原因很实在安装包构建工具对引号处理非常敏感路径一旦带空格bat 脚本里到处要加引号少加一个就是一场排查事故。# 解压 wix311-binaries.zip 到 C:\wix311路径里不要有空格和中文 Expand-Archive -Path .\wix311-binaries.zip -DestinationPath C:\wix311 # 把 bin 目录临时加进当前会话的 PATH $env:Path ;C:\wix311\bin # 验证工具可用第一行会输出编译器版本号 candle.exe -?如果想把 PATH 永久写入系统环境变量用 setx 命令但要注意 setx 会覆盖式地写用户变量操作前先记录原值。在 CI 里我更推荐的做法是不改 PATH直接在 bat 里用全路径引用比如 %WIX%\candle.exe这样脚本对执行环境无依赖。后面的命令都以“C:\wix311\bin 已在 PATH 中”为前提。验证标准是 candle.exe -? 能打印出版本信息看到 3.11 开头的版本号就说明包没问题。3.2 写一个最小可过编译的 wxs固定 ProductId放开 ComponentGuidwxs 是 WiX 的源文件本质是一份 XML。下面这个文件是能通过编译的最小骨架包含产品信息、目录结构、一个组件和一个功能?xml version1.0 encodingUTF-8? Wix xmlnshttp://schemas.microsoft.com/wix/2006/wi Product IdB8A4E2C0-6D7F-4E9A-9C3D-1F2A5B7E8D4A NameMyApp 主程序 Language1033 Version1.0.0.0 ManufacturerExample Corp UpgradeCodeA3F9C1E8-2B4A-4D5C-9E6F-7D8A9B0C1D2E Package InstallerVersion500 Compressedyes / MediaTemplate EmbedCabyes / Directory IdTARGETDIR NameSourceDir Directory IdProgramFiles64Folder Directory IdINSTALLFOLDER NameMyApp Component IdMainExecutable Guid* Win64yes File IdMainExe Source$(var.BuildDir)\MyApp.exe KeyPathyes / /Component /Directory /Directory /Directory Feature IdProductFeature Title主程序 Level1 ComponentRef IdMainExecutable / /Feature /Product /Wix逐一说明关键点。Product 的 Id 和 UpgradeCode 必须用固定 GUID不要偷懒写星号。原因UpgradeCode 是产品升级时检测旧版本的身份标识ProductId 决定已安装产品是否被识别为“同一个产品”。如果两处都写成 *每次构建都会生成新 GUID版本升级永远变成“安装第二个应用”旧版本不会被覆盖。所以这两个值在工程创建时生成一次之后不再变动。Component 的 Guid 写成 * 则恰恰相反这是 WiX 推荐的做法。星号让 WiX 基于组件路径确定性生成 GUID同一路径每次构建结果一致既免去手工维护上百个 GUID 的负担又保证升级时组件 ID 稳定旧版本能干净卸载。这里不要理解成“每次编译随机生成”它是确定性的算法结果。File 元素里 KeyPathyes 表示这个文件是组件的关键路径安装检测时以它是否存在来判断组件是否已安装。Source 写的是 $(var.BuildDir)\MyApp.exe这个变量由编译命令里的 -dBuildDir 提供不在 wxs 里写死绝对路径。提示WiX 3.10 之后推荐用 代替手写 。InstallerVersion500 表示要求系统安装服务不低于 5.0Windows 7 SP1 及以上均满足同时让 MSI 表结构更精简。这个骨架里目录用了 ProgramFiles64Folder 且组件带 Win64yes是标准的 64 位安装包写法。如果你要出 32 位包把目录改回 ProgramFilesFolder、去掉 Win64 属性即可。3.3 编译、链接与批处理脚本把两个命令串成一个构建步骤candle 负责把 wxs 编译成 wixobjlight 负责把 wixobj 链接成 msi。下面是完整的构建脚本可以直接存成 build.bat在 CI 的 cmd 步骤里执行echo off set WIXC:\wix311\bin %WIX%\candle.exe -nologo -dBuildDirrelease\bin -out build\MyApp.wixobj MyApp.wxs if errorlevel 1 exit /b 1 %WIX%\light.exe -nologo -out build\MyApp.msi build\MyApp.wixobj if errorlevel 1 exit /b 1 echo MSI built: build\MyApp.msi先看 candle 这一行。-nologo 去掉横幅-dBuildDirrelease\bin 把变量 BuildDir 指向实际的成品目录wxs 里的 $(var.BuildDir) 在这里被替换-out 指定 wixobj 的输出位置。这里要注意变量名大小写必须和 wxs 里引用的一致BuildDir 和 buildDir 在 WiX 预处理阶段是两个不同的变量。再看 light 这一行。-out 指定目标 msi 路径后面跟的是 candle 产出的 wixobj。如果工程引用了扩展比如之后要加的 WixUI在这一行追加 -ext 参数即可。脚本里每步之后检查 errorlevel 是 CI 里的关键习惯。candle 成功返回 0任何编译错误都会是非零值不检查的话light 会拿着不完整的 wixobj 继续跑报一堆难懂的链接错误把你真正的问题淹没掉。所以两个 errorlevel 判断不可省。另一种常见做法是不用 -d 变量给 light 加 -b 参数指定绑定路径wxs 里写相对路径。但那样做可移植性差目录结构一变就要改脚本。我更习惯 -d 变量方案把路径选择权留给调用方wxs 永远只描述“安装成什么样”不描述“文件从哪里来”。构建完成后build\MyApp.msi 就是产物。这里顺便提一句排查思路如果构建失败先看是 candle 报的错还是 light 报的错。candle 报错基本是 XML 语法、变量未定义、GUID 格式非法light 报错基本是引用关系、扩展缺失、ICE 校验失败。两个阶段的错误原因几乎不交叉按这个方向查能省一半时间。4. 常见问题与避坑清单wix311 构建中我踩过的五个坑4.1 装了 64 位程序结果文件落在 SysWOW64x64 架构的三件套缺失现象用 64 位编译环境产出的 MSI安装时文件进了 Program Files (x86) 目录程序也以 32 位进程在跑。检查 wxs 里的目录声明明明写的是 ProgramFilesFolder但 WiX 在 x86 默认模式下把它解析成了 32 位视图。原因candle 命令行缺少 -arch x64导致整个产品按 32 位处理。在 WiX 里64 位安装包不是“加几个属性”就能声明的它要求三件事同时成立candle 加 -arch x64、组件声明 Win64yes、目录用 ProgramFiles64Folder。缺一件系统就会走 32 位重定向逻辑。解决按下面三处逐一核对。Component IdMainExecutable Guid* Win64yes File IdMainExe Source$(var.BuildDir)\MyApp.exe KeyPathyes / /Component同时确认 wxs 里的目录节点是 ProgramFiles64Folder而不是 ProgramFilesFolder确认编译命令里带了 -arch x64。这三件套缺一不可这是我见过最多的翻车点没有之一。4.2 light 阶段 ICE57 报错64 位组件里混入了 32 位注册表键现象light 链接时构建失败日志里出现 ICE57 开头的错误大意是某个组件同时涉及 64 位文件键路径和会被重定向的 32 位注册表路径。构建进程直接退出退出码非零。原因ICE 是 light 阶段的内部一致性校验专门检查安装包规则层面的矛盾。一个标记为 Win64yes 的组件里如果 Registry 项写的是 HKEY_LOCAL_MACHINE\Software... 这种默认视图路径Windows 在安装时会把它重定向到 SysWOW64 对应的注册表视图与组件自身的 64 位键路径产生冲突。ICE 校验不允许这种同一组件内混合位架构的做法。解决把注册表项从 64 位组件里拆出来单独建一个 32 位组件专门放注册表写入或者给注册表键名加上 **64 后缀明确写到 64 位注册表视图。另外值得知道的是light 提供 -sice:ICE57 这样的临时压制参数但在团队工程里不要轻易用它。压制一个 ICE 等于关掉一道安全闸门当前构建能过升级时可能埋雷。正确顺序永远是先改 wxs 结构最后才考虑压制。4.3 中文界面变乱码或解析失败wxs 编码必须统一 UTF-8现象安装界面上的中文说明变成一串问号或者更严重light 直接报 XML 解析错误说文件里有非法字节。代码在同事机器上编译正常换到你的机器就挂。原因Windows 默认文本编码历史遗留严重wxs 文件被存成了 ANSI 或 GBK但文件头部的 XML 声明写的还是 encodingUTF-8。candle 按 UTF-8 去读读到中文字节就解码失败或者更隐蔽地解码出错误字符界面显示乱码。解决所有 wxs 统一使用 UTF-8 编码保存。带不带 BOM 都可以重点是文件实际字节必须是 UTF-8。用 VS Code 打开文件后看右下角编码提示不是 UTF-8 就“通过编码保存”重新存一次。团队协作时最好在编辑器设置里把默认文件编码固定为 UTF-8这个设置能避免 90% 的乱码问题。顺带提醒wxs 里的注释、Product 的 Name 属性、Feature 的 Title任何出现中文的地方都是编码重灾区检查时别只盯正文。4.4 下载源和杀软误报只从官方渠道拿 zip 并校验哈希现象从某个下载站拿到的 wix311-binaries.zip解压后被 Windows Defender 报毒或者解压出来的 bin 目录里多出几个不认识的 execandle.exe 的图标和官方的看起来不一样。原因WiX 工具要写注册表、创建安装服务、生成 MSI 文件行为特征和恶意安装包脚本有相似之处部分杀软会按启发式规则给出风险提示。另一个更危险的因素是很多第三方镜像站喜欢把多个工具混在一个压缩包里重新打包夹带私货不容易被注意。解决只从官方渠道获取 wix311-binaries.zip认准 WiX 工具集的官方发布仓库或者说 wixtoolset.org 的下载跳转页面不要从任何第三方“绿色工具合集”下载。拿到文件后先算哈希和官方发布页公布的 SHA256 比对Get-FileHash .\wix311-binaries.zip -Algorithm SHA256 | Format-List哈希一致才能确认这个包没有被替换过。如果此时杀软仍报毒可以把这个官方来源的包加入排除项。哈希对不上立刻删除重新下载。这个习惯花三十秒能省掉一整天的排毒时间。4.5 unresolved reference-ext 没加导致的链接失败现象light 阶段报类似这样的错误light.exe : error LGHT0104 : unresolved reference to symbol WixUI:WixUI_Minimal in section Product。构建中止。原因wxs 里使用了某个扩展程序集提供的符号但 light 命令行没有加载对应的扩展 DLL。WiX 的 -ext 机制和链接库的概念非常像你在代码里引用了库里的函数链接时不带那个库文件自然报未解析引用。这里引用的是 WixUI 扩展里的对话框集合但 light 不知道去哪里找。解决在 light 命令里补上扩展参数用 WixUI 就加载 WixUIExtensionlight.exe -ext WixUIExtension -nologo -out build\MyApp.msi build\MyApp.wixobj如果报错的符号前缀是 WixUtil:对应的是 WixUtilExtension前缀是 WixUI:对应 WixUIExtension前缀是 WixVaultExtension 等同理。另一个经验是这类错误直接搜错误码 LGHT0104比搜整句报错有效得多。错误码定位到具体错误类型搜索得到的答案更准确这个技巧对 WiX 全系列报错都适用。5. 给 MSI 加上 WixUI 向导用安装日志验证产物的最后一公里5.1 三行改动换来一个标准安装向导没有 UI 引用的 MSI 安装时是系统默认的用户账户控制弹窗加一个朴素进度条对内部工具够用但要交付给非技术同事时最好有一个标准的安装向导界面。WiX 3.11 自带 WixUI 扩展要做的事情只有两步。第一步在 wxs 的 Package 元素之后加一行 UI 引用!-- 在 Package ... / 之后、MediaTemplate ... / 之前插入 -- UIRef IdWixUI_Minimal /第二步light 命令加载对应扩展light.exe -ext WixUIExtension -nologo -out build\MyApp.msi build\MyApp.wixobj三种常见 UI 方案按需选择UI 方案交互范围适用场景WixUI_Minimal只有进度和完成页无交互内部工具、无选项安装WixUI_InstallDir增加安装路径选择页用户需要换目录WixUI_FeatureTree增加功能树选择页多组件、多特性产品大多数内部工具选 Minimal 就够了少一个交互页少一份用户误操作的可能。需要中文界面时在 light 里追加 -cultures:zh-CN 参数并把 Product 的 Language 改成对应 LCID扩展内置的本地化字符串会自动匹配。5.2 安装日志 /l*v最诚实的产物验证方式构建成功不等于安装成功。MSI 装上后文件到底落在哪里、注册表写了没有、为什么不声不响地回滚了这些问题用安装日志验证最直接。msiexec /i build\MyApp.msi /l*v install.log/l*v 是详细日志开关会记录安装过程中每个 Action 的执行顺序和返回值。我一般看日志只看三处第一搜 Return value 3这是失败返回值出现就说明某个 Action 崩了第二看最后一个 Action start 有没有对应的 Action ended没有配对的就是中断位置第三在日志里搜文件名比如 MyApp.exe看它实际被复制到了哪个目录程序装没装对地方日志比界面诚实得多。从那以后我每次在 CI 出完包都会强制自己把 /l*v 日志扫一遍再交付。装没装上、装到哪、哪一步失败这些问题的答案全在日志里猜是猜不出来的。希望帮到你。本文还有配套的精品资源点击获取
返回列表