前言
由于项目需要,需要.net7开发的winform应用程序进行打包生成msi安装包,因为是.net7的项目,所以选择了wix进行安装包的打包处理,但是网上搜到的相关的技术文档都比较老,而且多数使用的wix3.0版本对.net framework的winform应用程序的打包。
经过阅读官网,终于实现了打包的功能,于是将自己实现方式做了一个整理。
2024年5月16日:新增安装路径的设置
参考链接:
链接: wix官网, firegiant官网
一、准备打包环境
在Visual Studio的扩展下的选中管理扩展,搜索HeatWave,然后选中进行安装(可能存在IDE中无法下载的情况,可以直接去网页https://marketplace.visualstudio.com/vs搜索下载),我这里用的是Visual Studio 2022,所以安装的HeatWave for VS2022。
二、准备待打包的exe项目
因为只是写文档,所以只是创建了一个简单的.net7环境的控制台应用程序。
三、新建打包项目
1.新建打包项目
右键解决方案名,选择添加新建项目
语言栏选择WIX,模板选择MSI Package
2.对打包项目文件的简单说明
新建的打包项目会默认生成四个文件,三个.wxs文件和一个.wxl文件
2.1.Package.wxs
代码如下:
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="TestPackage" Manufacturer="TODO Manufacturer" Version="1.0.0.0" UpgradeCode="2e324481-3f22-4cf5-b7af-38ccb202c780">
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
<Feature Id="Main">
<ComponentGroupRef Id="ExampleComponents" />
</Feature>
</Package>
</Wix>
.wxs文件是使用xml语言编写的,顶层节点是Wix。
Package节点:在Wix节点下,Package节点包含了四个属性:
Name:打包生成的安装包名称,也是安装后在计算机“程序和功能”中的应用列表中显示的名称
Manufacturer:公司或组织名称
Version:版本号
UpgradeCode:是一个GUID值,标识生成的打包文件的唯一性
MajorUpgrade节点:是Package节点的子节点,表示安装的时候会去检测安装包版本,若安装版本比已安装版本小会给出提示,其属性DowngradeErrorMessage的值“!(loc.DowngradeError)”表示本地化的错误信息,DowngradeError是本地化提示设置的ID。本项目是指的引用“Package.en-us.wxl”文件中定义的DowngradeError的值
Feature节点:表示打包文件的特征,用于配置打包组件(要打包的文件、启动路径设置、桌面快捷菜单设置、开机启动设置)的引用,默认内置了<ComponentGroupRef Id="ExampleComponents" />
,其中ID指向了‘ExampleComponents.wxs’下的ID为‘ExampleComponents’的ComponentGroup
2.2.ExampleComponents.wxs
代码如下:
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="ExampleComponents" Directory="INSTALLFOLDER">
<Component>
<File Source="ExampleComponents.wxs" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Fragment 是碎片的意思,Wix考虑到会有很复杂的打包项目(不止存在一个exe,可能还有其他的dll,配置文件等需要打包),使用Fragment可以将待打包的项按照分类拆分成组件组,便于维护理解。
ComponentGroup是组件组的意思下面可以有很多Component,或者Files,ComponentGroup包含了两个属性:
ID:ComponentGroupRef引用的时候绑定的ID的值
Directory:组件组文件在安装的时候安装的位置对应的设置的ID,一般在‘Folders.wxs’文件中进行定义
Component是组件,下面包含一个File
File:组件包含的文件,支持相对地址,绝对地址,<File Source="ExampleComponents.wxs" />表示打包安装后的安装路径下会存在一个‘ExampleComponents.wxs’的文件
Files是文件集合,Include值的格式为‘路径**’表示路径下的所有文件都会被打包
2.3.Folders.wxs
代码如下:
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="!(bind.Property.Manufacturer) !(bind.Property.ProductName)" />
</StandardDirectory>
</Fragment>
</Wix>
Folders定义了安装路径,其中“ProgramFiles6432Folder”是内置的默认路径,根据安装包项目的平台设置决定是安装到ProgramFiles文件夹下还是ProgramFiles(x86)文件夹下,!(bind.Property.Manufacturer)
是Package.wxs中提到的公司名,!(bind.Property.ProductName)
是Package.wxs中提到的打包安装包名称
2.4.Package.en-us.wxl
代码如下:
<!--
This file contains the declaration of all the localizable strings.
-->
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
<String Id="DowngradeError" Value="A newer version of [ProductName] is already installed." />
</WixLocalization>
这是一个本地化的配置文件,用于配置本地化语言的提示信息
3.新建打包项目
在打包项目build之前会需要先build待打包exe项目
添加步骤:
右键打包项目下的依赖项,选择Add Project Reference
选择待打包项目
点击确定
4.编辑打包项目下的wxs文件
将项目中的ExampleComponents修改为TestComponents
4.1.添加待打包文件
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="TestComponents" Directory="INSTALLFOLDER">
<Component>
<Files Source="Test.exe" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
像上面这样修改TestComponents.wxs打包后的安装包安装后只会存在一个Test.exe文件,而我们特意引用的Newtonsoft.Json是没有打进包里的
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="TestComponents" Directory="INSTALLFOLDER">
<Files Include="..\Test\bin\Debug\net7.0\**" />
</ComponentGroup>
</Fragment>
</Wix>
像上面这样修改后,就会将待打包应用程序生成的所有文件都打包成安装包,但是还是可以看到打包生成的安装包拆分成了三个文件分别是.cab、.msi以及.wixpdb,其中.wixpdb跟.pdb是一样的是调试用的文件可以忽略。
修改Package.wxs,在MajorUpgrade节点后添加新节点如下,这样就会将生成的.cab压缩包合并入.msi,减少打包文件数量,可以注意.msi文件大小的变化
<MediaTemplate EmbedCab="yes" />
4.2.添加桌面快捷方式以及开始菜单项
在打包项目下新建Icons文件夹,在文件夹下放入logo.ico,做为快捷方式显示的时候使用的图标
然后再修改Package.wxs,在Package节点下添加
<Icon Id="appIcon" SourceFile="Icons\logo.ico" />
新建‘ShortcutsComponent.wxs’文件
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<Component Id="ShortcutsComponent" Directory="INSTALLFOLDER" Guid="FB26D5E1-571C-44F5-9FA2-CEFB7F42646E">
<Shortcut Id="StartMenuShortcut" Directory="StartMenuFolder" Target="[INSTALLFOLDER]Test.exe" Name="Test" WorkingDirectory="INSTALLFOLDER" Icon="appIcon" />
<Shortcut Id="DesktopShregeditortcut"
Directory="DesktopFolder"
Target="[INSTALLFOLDER]Test.exe"
Name="Test"
WorkingDirectory="INSTALLFOLDER"
Icon="appIcon" />
<RemoveFolder Id="CleanUpShortCut"
Directory="INSTALLFOLDER"
On="uninstall" />
<RegistryValue Root="HKCU"
Key="Software\!(bind.Property.Manufacturer)\!(bind.Property.ProductName)"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes" />
</Component>
</Fragment>
</Wix>
再修改Package.wxs 在Feature下添加对ShortcutsComponent的引用
<ComponentRef Id="ShortcutsComponent" />
4.3.注册开机启动
新建‘RegisterComponent.wxs’文件
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<Component Id="RegisterComponent" Directory="INSTALLFOLDER" Guid="6B04AF0C-E268-4E8F-B1F6-ABA64B912F1D">
<RegistryValue Root="HKCU"
Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Name="Test"
Type="string"
Value="[INSTALLFOLDER]Test.exe" />
<RemoveRegistryValue Root="HKCU" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Run" Name="Test"/>
</Component>
</Fragment>
</Wix>
再修改Package.wxs 在Feature下添加对RegisterComponent的引用
<ComponentRef Id="RegisterComponent" />
四.如何在安装应用程序之前检测安装环境是否满足,以及内置运行.net 环境的安装
我上面的添加的待打包项目是在.net7.0的项目,因此打包安装之前需要先检测程序运行环境是否满足
4.1.新建Bundle项目
右键解决方案选择添加=》新建项目
语言栏选择WiX,模板选择Bundle
点击下一页,输入项目名称,新建Bundle项目
4.2.将下载好的运行环境放入Bundle项目中
右键Bundle项目,选择添加=》新建文件夹
文件夹命名为Assets
将下载好的运行环境安装exe拷贝到Assets文件夹下
4.3.添加对打包项目的引用
添加对打包项目的引用可以在Bundle项目生成的时候先生成打包项目
右键Bundle项目下的依赖项
选择Add Project Reference
选择打包项目,这里是TestPackage,点击OK
4.4.添加Nuget扩展
要判断是否安装有指定的.net环境,需要使用wix的netfx扩展
右键Bundle项目下的依赖项
选择管理Nuget程序包
输入wix过滤
选择‘WixToolset.Netfx.wixext’扩展进行安装
4.5.编辑Bundle.wxs文件
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"
xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx">
<Bundle Name="Test" Manufacturer="TODO Manufacturer" Version="1.0.0.0" UpgradeCode="0025b14f-f1f9-4ae8-a46c-35630e0a4276">
<BootstrapperApplication>
<bal:WixStandardBootstrapperApplication LicenseUrl="https://www.example.com/license" Theme="hyperlinkLicense" />
</BootstrapperApplication>
<netfx:DotNetCoreSearch RuntimeType="desktop" MajorVersion="7" Platform="x64" Variable="DOTNETDESKTOPVERSION"/>
<Chain>
<ExePackage Id="DotNetDesktop705"
DetectCondition="DOTNETDESKTOPVERSION >= "7.0.5""
Permanent="yes"
Vital="no"
InstallArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /install /quiet /norestart"
RepairArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /repair /quiet /norestart"
UninstallArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /uninstall /quiet /norestart">
<ExePackagePayload
Name="windowsdesktop-runtime-7.0.5-win-x64.exe"
SourceFile="Assets\windowsdesktop-runtime-7.0.5-win-x64.exe"
/>
</ExePackage>
<!-- TODO: Define the list of chained packages. -->
<MsiPackage SourceFile="TestPackage.msi" />
</Chain>
</Bundle>
</Wix>
引用‘WixToolset.Netfx.wixext’扩展后要在Wix节点添加属性值xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx"
使用下面这段代码用来查询系统是否安装了指定的.net core 环境,下面这段代码表示判断是否安装了.net7的x64的桌面程序运行环境,未安装则根据ExePackage
中配置的安装包进行安装
<netfx:DotNetCoreSearch RuntimeType="desktop" MajorVersion="7" Platform="x64" Variable="DOTNETDESKTOPVERSION"/>
<MsiPackage SourceFile="TestPackage.msi" />
,这里隐含了一个知识点,因为我们项目中对TestPackage项目添加了引用,所以可以文件路径可以直接写TestPackage.msi,而不用使用相对路径
4.6.右键Bundle项目进行生成
4.7.安装效果
正在安装运行.net7环境
安装结果
五.修改安装路径
5.1 修改Package项目
打开NuGet安装包,新增对WixToolset.UI.wixext
的引用
5.1.1 Folders.wxs
注释掉StandardDirectory
这样安装路径就不会固定到ProgramFiles
文件夹
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<!--<StandardDirectory Id="ProgramFiles6432Folder">-->
<Directory Id="INSTALLFOLDER" Name="!(bind.Property.Manufacturer) !(bind.Property.ProductName)" />
<!--</StandardDirectory>-->
</Fragment>
</Wix>
5.1.2 Package.wxs
新增xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui
ui命名空间
新增 <ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER"/>
让安装界面可显示安装路径选择
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package Name="TestPackage" Manufacturer="TODO Manufacturer" Version="1.0.0.0" UpgradeCode="2e324481-3f22-4cf5-b7af-38ccb202c780">
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER"/>
<MediaTemplate EmbedCab="yes" />
<Icon Id="appIcon" SourceFile="Icons\logo.ico" />
<Feature Id="Main">
<ComponentGroupRef Id="TestComponents" />
<ComponentRef Id="ShortcutsComponent" />
<ComponentRef Id="RegisterComponent" />
</Feature>
</Package>
</Wix>
5.2 修改Bundle项目
去掉WixStandardBootstrapperApplication
使用WixInternalUIBootstrapperApplication
进行替代,否则会跳过msi
文件也就是package
中设置的安装界面,直接按默认路径进行安装
MsiPackage
节点新增bal:DisplayInternalUICondition="yes"
属性,展示package
中设置的ui界面
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"
xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx">
<Bundle Name="Test" Manufacturer="TODO Manufacturer" Version="1.0.0.0" UpgradeCode="0025b14f-f1f9-4ae8-a46c-35630e0a4276">
<BootstrapperApplication>
<bal:WixInternalUIBootstrapperApplication Theme="standard" />
<!--<bal:WixStandardBootstrapperApplication LicenseUrl="https://www.example.com/license" Theme="hyperlinkLicense" />-->
</BootstrapperApplication>
<netfx:DotNetCoreSearch RuntimeType="desktop" MajorVersion="7" Platform="x64" Variable="DOTNETDESKTOPVERSION"/>
<Chain>
<ExePackage Id="DotNetDesktop705"
DetectCondition="DOTNETDESKTOPVERSION >= "7.0.5""
Permanent="yes"
Vital="no"
InstallArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /install /quiet /norestart"
RepairArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /repair /quiet /norestart"
UninstallArguments="windowsdesktop-runtime-7.0.5-win-x64.exe /uninstall /quiet /norestart">
<ExePackagePayload
Name="windowsdesktop-runtime-7.0.5-win-x64.exe"
SourceFile="Assets\windowsdesktop-runtime-7.0.5-win-x64.exe"
/>
</ExePackage>
<!-- TODO: Define the list of chained packages. -->
<MsiPackage SourceFile="TestPackage.msi" bal:DisplayInternalUICondition="yes" />
</Chain>
</Bundle>
</Wix>
5.3 汉化
5.3.1 修改项目属性
右键项目进入项目属性设置,修改Building
中的Cultures to build
的值为zh-CN
,package
和bundle
项目都要修改
5.3.2 修改en-us.wxl文件
修改package项目下的en-us.wxl文件为zh-CN.wxl
<!--
This file contains the declaration of all the localizable strings.
-->
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="zh-CN">
<String Id="DowngradeError" Value="一个较新版本的 [ProductName] 已安装。" />
</WixLocalization>
5.4 安装效果
总结
以上就是我打包一个使用.net7.0开发的应用程序的全过程。