利用Wix打包安装包
- 背景
- 具体步骤
- 1、安装 WiX Toolset 工具集
- 2、安装 WiX Toolset 系列 Visual Studio 插件
- 3、创建Wix工程
- 4、添加工程文件
- 5、修改Product元素
- 6、修改Package元素
- 7、修改MajorUpgrade元素
- 8、修改Media属性
- 9、设置安装引导界面
- 10、添加WPF项目文件
- 11、添加桌面快捷方式
- 12、添加菜单快捷方式
- 13、设置安装引导界面语言环境
- 14、最后设置
- 源码
背景
将指定WPF项目利用Wix打包为安装包!
具体步骤
以打包名为 MRISoftwareDeployPlatform 的WPF项目为例,说明如何利用WIX将WPF打包为安装包。
1、安装 WiX Toolset 工具集
首先在GitHub发布页上下载并运行wix311.exe ,以部署 WiX Toolset 的构建环境。运行后点击中心的Install,安装成功后如下图
2、安装 WiX Toolset 系列 Visual Studio 插件
打开Visual Studio,点击 扩展 -> 管理扩展 -> 搜索框输入 wix -> 点击 Wix Toolset Visual Studio Extension并进行下载,之后按照提示进行安装即可。
若安装时出现下图所示情况,个人建议利用Microsoft Visual Studio Installer Projects进行打包(具体操作可见博客 ),因为要解决这个问题可能涉及到CA许可证等文件,挺麻烦。
若是下图所示情况,直接点击Modify即可。
3、创建Wix工程
在目标项目下,右键点击解决方案 -> 添加 -> 新建项目 -> 输入wix -> 选择 Setup Project for Wix v3并创建。
创建成功后,则如下图,此处创建的项目名为 MRIDPSetup,红框中内容为 Product.wxs 文件内容,而生成安装包则主要是在此文件上进行操作。
4、添加工程文件
在 References 处添加相关文件。此处第一个需添加的是 MRISoftwareDeployPlatform 工程,因为其是项目,所以需在项目此页面进行选择,如图
第二个则是WixUIExtension,此文件提供了安装时的UI引导页面,由于其是文件,因此需在上图的浏览项进行查找与选择,其路径大致为C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUIExtension.dll ,可在相似路径下进行添加。
两个文件添加成功后如图:
5、修改Product元素
根据需求,设置Product处的属性,如图
属性说明:
1)Id: 用于指定安装程序的 ProductCode,”*”指在构建安装程序时由 WiX 工具自动生成一个新的 GUID;
2)Name:为安装包名称,注册表也是保存当前配置;
3)Language:为区域性标志符(LCID),常见参数 1033 表示英语(美国),2052表示简体中文.以下是两种语言对应的Language与Codepage
4)Version:为安装包版本号;
5)Manufacturer:为生产厂商;
6)UpgradeCode:为升级编码,其内容格式为GUID,与上述Id的内容格式相同,便于后续配置升级策略。
6、修改Package元素
根据需求修改Package属性,修改后如下图:
属性说明:
1) InstallerVersion:指定安装程序引擎的最低版本要求。”200”表示需要MSI 安装引擎Windows Installer 2.0 或更高版本;
2) Compressed: 指定是否要对安装包进行压缩,若为 yes,则安装包中的文件将被压缩;
3) InstallScope: 指定安装范围,值可选perMachine 或者 perUser,前者针对使用这个机器的所有用户,后者则针对安装包的运行用户;
4) Keywords: 指定与安装包相关的关键字,便于搜索和组织;
5) Comments: 提供对安装包的注释或说明。
7、修改MajorUpgrade元素
元素用于定义主要升级(Major Upgrade)的行为,常用于配置软件的升级策略。
因暂未涉及该需求,故而此处只需修改MajorUpgrade下的DowngradeErrorMessage属性。该属性用于指定在尝试安装较低版本的产品时显示的错误消息。可根据需求进行设计。此处修改内容如下
<MajorUpgrade DowngradeErrorMessage="A newer version of MRISoftwareDeployPlatform is already installed." />
8、修改Media属性
MediaTemplate 与 Media 元素的差别为: Media 元素用于定义实际的安装介质,而 MediaTemplate 元素则用于定义多媒体安装的模板,以便在构建过程中生成多个 Media 元素。
由于此处只需实现将存档文件(Cabinet 文件)嵌入到生成的安装包(MSI 文件)中,以保证安装文件的简洁,而该功能使用 Media即可实现,所以将注释掉原有的 MediaTemplate元素,并以 Media 进行替换。之后再将Media元素中的 EmbedCab 属性置为“yes”。修改结束后的代码如图,其中
9、设置安装引导界面
基础信息:
Wix提供了5种UI风格:WixUI_Advanced、WixUI_FeatureTree、WixUI_InstallDir (带有选择安装目录)、WixUI_Minimal (简洁安装) 和 WixUI_Mondo (自定义模块安装).可根据自己需求利用 UIRefe 进行声明使用.
Property 为安装配置特性,其中 Id: 属性的名称 ,Value: 属性的参数。
常用Id参数:
WIXUI_INSTALLDIR 安装默认路径;
WIXUI_EXITDIALOGOPTIONALTEXT 安装成功界面显示文字;
WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT 安装成功界面添加复选框(例如安装完成打开软件);
WixShellExecTarget 执行事件的目标,需要和执行事件一起使用,若不需要执行事件可不用在意此参数。
具体设置:在 Product 下添加上述信息,如图
此处为了跳过许可协议这一引导界面,便采用了Publish(Publish 元素允许基于某些条件来触发或禁用特定的动作,从而实现根据用户的环境或选择进行动态控制的功能)进行操作,如上图,其中Order属性指定了页面的顺序,在WixUI_InstallDir 此风格下,许可协议这一页面处于第二顺序,所以此处手动设定以跳过此页面。
其实也可以通过这种方式去设置引导界面的先后顺序,只要知道目标UI风格下各页面的顺序便可进行相关操作。
10、添加WPF项目文件
wix打包时需要手动添加相关文件。此次的目标项目中,需要将一些脚本、python包安装包等文件进行打包,且需将其专门放置于一个文件夹(命名为“Scripts”)下,防止安装后文件过多影响使用。
一些元素说明:
(1)Feature 元素用于定义安装包中的功能,而功能是一组相关的组件,可选择性地安装或卸载。其中属性:
1)Id: 指定功能的唯一标识符;
2)Title: 指定用户界面中显示的标题;
3)Level: 指定功能的初始安装级别,0 表示未安装,1 表示安装;
4)ComponentGroupRef : 引用一个组件组,将该组件组包含在当前功能中。组件组是一组相关的组件的集合,它们可以一起进行安装或卸载。其通常与 ComponentGroup对应,前者为引用,后者为本体;
5)ComponentRef : 引用一个单独的组件,将该组件包含在当前功能中。其与Component对应。
(2)Fragment 元素是 WiX 中用于组织代码、实现模块化和条件设置的关键元素之一。该元素内部可以包含对其他文件或外部模块定义的组件的引用,通过这种方式将不同的组件组织在一个逻辑单元内。
通过合理使用 元素,可以使 WiX 项目更加清晰、可维护,并支持更灵活的构建和条件性添加组件的需求。
如下图,其中var.MRISoftwareDeployPlatform.TargetDir则指代了目标安装路径,此参数的值是需要在安装时手动选择后才可最终确定。图中ProductComponents在wix项目中默认存在,其保存的是WPF项目相关的exe和dll,而Scripts则保存的是脚本与python包安装文件。
如图所示:
11、添加桌面快捷方式
此处使用的是Directory进行快捷方式添加。先在Feature 中添加DesktopFolderShortcut,此为桌面快捷方式。
之后在Id为 TARGETDIR 的 Directory节点 中设置具体内容。节点说明:
1)DesktopFolder 指桌面文件夹,Windows默认值
2)Shortcut 节点表示快捷方式
- Id: 唯一标识
- Name: 快捷方式名称
- Description: 快捷方式描述
- Target: 快捷方式目标文件
- WorkingDirectory:指工作区域
- Arguments: “/x [ProductCode]”,是传递给目标文件的命令行参数。[ProductCode] 会在运行时被替换为当前安装的产品代码。
3)RemoveFolder 用于在卸载时删除指定的目录
- Id: 指定要删除的目录的标识符。
- On: 指定在何时执行删除操作。在这个例子中,On=“uninstall” 意味着在执行卸载操作时执行删除操作。
4)RegistryValue 用于在注册表中设置一个值
- Root: 指定注册表项的根键。
- Key: 指定注册表项的路径。
- Name: 指定注册表项的名称。
- Type: 指定注册表项的数据类型。
- Value: 指定注册表项的值。
- KeyPath: 指定是否将该注册表项视为产品的关键路径,用于检测产品是否已安装。
具体代码设置如下
12、添加菜单快捷方式
与桌面快捷方式不同,创建菜单快捷方式时需要在菜单目录下添加一个目录(可根据自己需求设置)。相较于桌面快捷方式,首先需更换目标路径,即ProgramMenuFolder表示菜单目录,而菜单快捷方式的其他设置类似于桌面快捷方式,具体可查看下图。但下图中还添加了一个卸载快捷方式,同存于所添加的目录下。
其中,msiexec.exe是Windows系统自带的卸载软件,而 [SystemFolder] 则指代Windows默认系统路径;Arguments属性的设置中,[ProductCode]指代了当前这个wix项目生成后的Code信息,msiexec.exe主要是通过此Code信息来指定卸载安装后的软件。
13、设置安装引导界面语言环境
wix项目生成的安装包默认是英文环境,为满足大众需求,通常需要将其转换为中文环境。
首先在wix项目下添加一个 Localization File,命名为WixUI_zh-cn. 此类文件,即Wxl文件相当于应用程序的资源文件。
之后设置WixLocalization节点属性,中文的Codepage 为 936, Culture为 zh-cn。
再如图进行设置,其中 Overridable 设为 yes,则表示可以重写。因为默认是英文,可以重写的意思就是最新的String会覆盖之前的同名Id。
14、最后设置
若未进行第8步骤的操作,则整个wix项目生成后会输出三个文件,即
(1)MSI 文件 (.msi):
作用: MSI 文件是最终生成的安装包,包含了应用程序的安装信息、文件、注册表设置、服务等。它是 Windows Installer 技术的标准安装包格式。当用户运行 MSI 文件时,Windows Installer 服务会负责将应用程序正确地部署到计算机上,并执行必要的配置和安装步骤。MSI 文件是用于实际部署应用程序的主要文件。用户可以通过双击 MSI 文件来安装、修复、卸载应用程序。
(2)CAB 文件:
作用: CAB 文件是压缩文件,其中包含了在安装期间需要的文件。这些文件可能包括应用程序的二进制文件、资源文件等。CAB 文件通常用于减小 MSI 文件的大小,提高传输效率。 当用户运行 MSI 文件时,其中的 CAB 文件会被提取,并包含的文件将被解压缩到计算机上。这有助于减少网络传输时间和占用磁盘空间。
(3)WiXPDB 文件 (.wixpdb):
作用: WiXPDB 文件是 WiX 项目的构建数据库文件,包含了 WiX 项目的构建信息、源文件列表、目标文件等。这个文件对于调试和了解 WiX 项目的结构和构建过程非常有用。WiXPDB 文件主要用于开发人员进行调试和分析构建过程。虽然不是必需的,但在需要深入了解项目的构建细节时,这个文件可以提供有用的信息。
而进行了第8步骤中所说的内容,则cab文件会被集成到msi文件中。此时项目生成后的文件则还有一个wixpdb文件。
为了降低文件数量,还需在项目属性界面的Build下勾选Suppress output of the wixpdb files,如图,此操作会禁止输出wixpdb文件。如此,项目生成后则会只有一个msi文件。
源码
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MRIDPSetup"
Language="2052"
Codepage="936"
Version="1.0.0.0"
Manufacturer="xxxxxx"
UpgradeCode="05edbaec-c8fb-4bab-82ff-1fa75a55cd10">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Keywords="MRIDP"
Comments="xxxxxxxxxx"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of MRISoftwareDeployPlatform is already installed." />
<!--<MediaTemplate />-->
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Feature Id="ProductFeature" Title="MRIDPSetup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="Scripts" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="DesktopFolderShortcut" />
</Feature>
<UI>
<UIRef Id="WixUI_InstallDir" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="1">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="3">1</Publish>
</UI>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="Thank you for installing this product." />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch this Application " />
<UIRef Id="WixUI_Common" />
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<!-- 在安装路径下添加Scripts文件夹 -->
<Directory Id="INSTALLFOLDER" Name="MRIDPSetup">
<Directory Id="Scripts" Name="Scripts"/>
</Directory>
</Directory>
<!-- 添加菜单栏快捷方式 -->
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="MRISoftwareDeployPlatform">
<Component Id="ApplicationShortcut" Guid="*">
<Shortcut Id="ApplicationStartMenuShortcut" Name="MRISoftDP"
Description="xxxxxxxxx"
Target="[INSTALLFOLDER]MRISoftwareDeployPlatform.exe"
WorkingDirectory="INSTALLFOLDER">
</Shortcut>
<Shortcut Id="ApplicationUninstallShortcut"
Name="Uninstall"
Description="Uninstall"
Target="[SystemFolder]msiexec.exe"
Arguments="/x [ProductCode]"/>
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\MRISoftwareDeployPlatform" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<!-- 添加桌面栏快捷方式 -->
<Directory Id="DesktopFolder">
<Component Id="DesktopFolderShortcut" Guid="*">
<Shortcut Id="AppDesktopShortcut" Name="MRISoftDP"
Description="xxxxxxxx"
Target="[INSTALLFOLDER]MRISoftwareDeployPlatform.exe"
WorkingDirectory="INSTALLFOLDER">
</Shortcut>
<RemoveFolder Id="DesktopFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Neusoft_MR\MRISoftwareDeployPlatform" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent" Guid="{7B576F74-DE0A-4125-90A2-AF83FD9891B0}">
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)MRISoftwareDeployPlatform.exe" Id="MRISoftwareDeployPlatform.exe"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)MRISoftwareDeployPlatform.exe.config" Id="MRISoftwareDeployPlatform.exe.config"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Newtonsoft.Json.dll" Id="Newtonsoft.Json.dll"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)System.Buffers.dll" Id="System.Buffers.dll"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)System.Memory.dll" Id="System.Memory.dll"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)System.Numerics.Vectors.dll" Id="System.Numerics.Vectors.dll"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)System.Runtime.CompilerServices.Unsafe.dll" Id="System.Runtime.CompilerServices.Unsafe.dll"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="Scripts" Directory="Scripts">
<Component Id="Scripts" Guid="{4F3A2FE1-6190-4A32-8D8C-594DCB527480}">
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\demjson-2.2.4.tar.gz" Id="demjson_2.2.4.tar.gz"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\InstallPythonPackages.bat" Id="InstallPythonPackages.bat"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\jsonpath_ng-1.5.3-py3-none-any.whl" Id="jsonpath_ng_1.5.3_py3_none_any.whl"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\jsonpath-0.82.2.tar.gz" Id="jsonpath_0.82.2.tar.gz"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\KillAll.ps1" Id="KillAll.ps1"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\pip-19.3.1-py2.py3-none-any.whl" Id="pip_19.3.1_py2.py3_none_any.whl"/>
<File Source="$(var.MRISoftwareDeployPlatform.TargetDir)Scripts\pyodbc-4.0.30-cp36-cp36m-win_amd64.whl" Id="pyodbc_4.0.30_cp36_cp36m_win_amd64.whl"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>