65.9K
CodeProject 正在变化。 阅读更多。
Home

创建本地化的 Windows Installer 和引导程序:第 1 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (9投票s)

2010年8月22日

CPOL

6分钟阅读

viewsIcon

46669

downloadIcon

753

本系列文章是使用一些实际需求构建可本地化 Windows Installer & Bootstrapper 的完整端到端解决方案。

引言

距离我上一篇文章已经有一段时间了,所以我想写写我最近使用 Wix 创建 Windows Installer 包的经验。我上次使用 Wix 1.0 是在大约 4 年前,效果很好,我都忘记了使用它有多么令人满意。不幸的是,在我撰写本文时,Wix 3.5 尚未发布,但似乎有一些很棒的新功能正在开发中,包括备受期待的 _Burn_ 工具(引导编译器)以及对 Visual Studio 2010 的 Votive 支持。

在本系列文章中,我想根据我最近的经验,为 .NET 应用程序的部署提供一个完整的端到端解决方案。所提供的解决方案基于以下实际需求:

  • 提供一个 MSI 安装程序,用于部署 .NET 应用程序
  • 使用 NGen 预缓存 .NET 程序集以加快启动速度
  • 仅允许在特定操作系统上运行安装程序
  • 仅根据 WMI 查找结果运行安装程序
  • 仅在安装了先决条件(.NET 4.0)的情况下运行安装程序
  • 将版本信息和其他特定信息从构建脚本注入安装程序
  • 在一个 MSI 安装程序中提供本地化版本,并自动检测用户语言
  • 创建一个本地化的引导程序,作为单个文件安装必要的先决条件(.NET 4.0)

请注意,由于我在示例中针对的是 Wix 3.0 的 Votive 项目不支持的 Visual Studio 2010 项目,因此我改用 NAnt 脚本来自动化构建过程。

技术栈

关于 GUID 的说明

在处理 Wix 时,生成自己的 GUID 非常重要,以避免与其他产品发生冲突。由于复制粘贴示例很常见,我故意隐藏了我的 GUID,以防止重复项最终出现在生产代码中。要使用解决方案中的任何示例,请务必将所有格式为 NEWGUID-XXXX-XXXX-XXXX-XXXXXXXXXXXX 的 ID 替换为您自己的唯一 GUID。您会发现这些 ID 已为方便起见分解到 _Config.Wsi_ 中。

解决方案概览

MSI 安装程序和 EXE 引导程序都由一个 NAnt 脚本生成。该脚本执行以下步骤,这些步骤将在本系列文章中详细介绍。要构建项目,请执行随附的 _build.bat_ 文件。

  1. 使用 HEAT 生成 Wix 组件片段
  2. 转换 HEAT 输出以注入 NGen 元素
  3. 使用 CANDLE 编译 Wix 项目,注入构建变量
  4. 使用 LIGHT 链接器生成 MSI 的英文版本
  5. 使用 LIGHT 链接器生成 MSI 的语言变体
  6. 使用 TORCH 通过比较步骤 4 和 5 的输出来创建转换(增量)文件
  7. 使用 _WISUBSTG.VBS_ 将 MST 嵌入到原始 MSI 文件中
  8. 对每种支持的语言重复步骤 5-7
  9. 预处理 dotNetInstaller 配置文件以注入构建参数
  10. 编译引导程序

使用 HEAT

HEAT 工具用于生成 Wix 代码片段。该解决方案使用 HEAT 创建一个 Wix 组件片段,该片段描述了将由安装程序部署的目标文件。

由于我们希望安装程序将这些文件部署到用户选择的安装目录,因此我们需要将生成的组件元素嵌套在一个与此位置相关的特定目录节点中。幸运的是,HEAT 为此提供了一个参数,我们只需要使用 -dr 参数将正确的目录 ID 传递给工具。由于我们将使用 WixUI 库,因此安装目录 ID 必须设置为 WIXUI_INSTALLDIR

heat dir MyProject\bin\Release
     -o Component.wsx
     -dr WIXUI_INSTALLDIR
     -cg AppFiles 
     -var var.InputSourceFolder
     -g1 -gg -srd -sfrag

我们还传递 -cg 参数,以便 HEAT 输出一个带有特定 ID 的生成的 ComponentGroup。这允许我们从主 Wix 项目引用它并将其包含在 Wix 功能中。var 参数告诉 HEAT 将目标文件的绝对路径替换为自定义变量 var.InputSourceFolder。这允许我们在编译时定义目标文件的位置。

配置 NGen

本机映像生成器(Ngen.exe)是一个 .NET 工具,通过预缓存编译后的映像来提高托管应用程序启动时间的性能。通常,对于 .NET 应用程序,JIT 编译器会在首次运行时编译映像,因此会导致初始延迟。通过在安装过程中对应用程序运行 NGen,预缓存已编译的 .NET 应用程序映像,因此首次启动时执行速度会更快。

Wix 附带 WixNetFxExtension.dll 扩展,可用于配置 NGen 工具。要使用该扩展,我们只需将 netfx 命名空间添加到 _Main.wxs_ 项目文件中,并将 NativeImage 元素添加到我们想要预缓存的任何文件节点中。最后,我们必须确保在编译过程中将扩展传递给 CANDLE。

以下显示了 NGen 如何在组件定义中作为父文件节点的一个子节点进行配置。

<Component Id="..." Guid="....">
   <File Id="..." KeyPath="yes" Source="$(var.InputSourceFolder)\MyAppLibrary.dll">
      <netfx:NativeImage Priority="1" Id="MyAppLibrary.dll" 
	AppBaseDirectory="WIXUI_INSTALLDIR" />
   </File>
</Component>

但是,这里有一个问题,由于 HEAT 用于动态生成组件片段,我们该如何注入 NativeImage 节点呢?

幸运的是,Wix 以另一种参数的形式解决了这个问题,该参数可以传递给 HEAT 工具。此参数描述了一个可选的 XSLT 文件,该文件在输出期间应用于片段。这在解决方案中用于将 NativeImage 节点注入到扩展名为 DLLEXE 的所有文件的组件片段中。

我们的 HEAT 命令现在看起来像这样

heat.exe dir MyProject\bin\Release
     -o Component.wsx
     -dr WIXUI_INSTALLDIR
     -cg AppFiles 
     -g1 -gg -srd -sfrag
     -t:Components.xslt

WixUI 库

在提供的示例解决方案中,我使用的是 WixUI 库提供的标准 WixUI_MONDO 界面。这只需在 _Main.wsx_ 中添加以下引用即可。执行 LIGHT 时,必须包含 _WixUIExtension.dll_ 扩展。

   <UI>
      <UIRef Id="WixUI_Mondo" />
      <UIRef Id="WixUI_ErrorProgressText" />
   </UI>

在我们的解决方案中,我们还通过覆盖一些 WixUI 变量来替换库对话框使用的标准映像和协议文件。

    <WixVariable Id="WixUIBannerBmp" Value="binary\banner.bmp" />
    <WixVariable Id="WixUIDialogBmp" Value="binary\dialog.bmp" />
    <WixVariable Id="WixUILicenseRtf" Value="binary\EULA_en-gb.rtf" />

使用 WixUI_Mondo 时,源代码中定义的安装目标目录必须使用 ID WIXUI_INSTALLDIR。这与自动生成的 _Component.wsx_ 文件中的引用相关联,并允许用户通过 WixUI 界面覆盖位置。如果用户不自定义安装位置,则文件将部署到片段下方定义的“程序文件”文件夹中。这是使用在编译时注入的自定义变量来确定默认文件夹名称。

<Directory Id='TARGETDIR' Name='SourceDir'>
    <Directory Id='ProgramFilesFolder' Name='PFiles'>
      <Directory Id='ManufacturerDir' Name='$(var.CompanyName)'>
        <Directory Id='WIXUI_INSTALLDIR' Name='$(var.FriendlyName) 
	v$(var.ProductVersion)' />
      </Directory>
    </Directory>
      ... 
</Directory>

使用 CANDLE

Wix CANDLE 工具将 Wix 源文件编译成带有 WixOBJ 扩展名的中间文件格式。此时也可以注入构建参数,因为 Wix 支持 自定义定义变量。我们可以使用这些变量来传递产品名称和版本号,以及安装程序要包含的目标文件的位置。

CANDLE 命令看起来像这样

candle.exe -o bin\Release\
     -ext WixNetFxExtension.dll
     -dInputSourceFolder=MyProject\bin\Release\
     -dFriendlyName="My App"
     -dVersion=1.0.0.0
     Main.wxs 
     Component.wxs

在提供的解决方案中,您会注意到我正在使用 Wix 提供的 NAnt 任务来执行 CANDLE 命令。构建脚本能够从构建属性(可能来自 CruiseControl 等构建服务器)传递自定义变量。

使用 LIGHT

LIGHT 是用于从中间 WixObj 库生成 MSI 文件的链接器。在我们的解决方案中,这样做可以通过以下命令行完成,但同样,在解决方案中我们实际上使用的是 Wix NAnt 任务。

light.exe -o bin\Release\Setup.msi
     -ext WixNetFxExtension.dll
     -ext WixUIExtension.dll
     Main.wixobj 
     Component.wixobj

摘要

在这一部分,我们创建了一个功能齐全的 Windows Installer 文件,用于部署 .NET 应用程序。在 第 2 部分中,我将讨论添加条件检查以进一步增强安装程序。

历史

  • 2010年8月22日:初始发布
© . All rights reserved.