Big*_*ris 2 windows-installer wix admin conditional-statements
我们的产品有三种类型/口味,但只有一种用 WiX 编写的 MSI。当我们构建安装程序时,我们通过定义的常量传递风味:
Call MSBUILD.bat ..\MSIs\CoreProduct\OurProduct.sln /p:DefineConstants="FLAVOUR=%_Flavour%"
Run Code Online (Sandbox Code Playgroud)
常量在 Visual Studio 中设置,在 Build -> Define preprocessor variables as FLAVOUR=50 下。构建过程传递值 50、200 或 LITE 作为风味。
在 WiX 代码中,我们在组件上有很多条件,告诉它根据风格安装哪个文件;例如
<Component Id="cmp7F45920B1AA100729BAE37FC846B3FC5" Guid="*">
<File Id="fil238A776D9294E14671E012472F9F7196"
KeyPath="yes"
Source="$(var.MenusPath)\ClientListView 200.r5m"
<Condition>$(var.FLAVOUR)=200</Condition>
</Component>
<Component Id="cmp8BFF42B232724DC4BA5B8F87994DEF21" Guid="*">
<File Id="fil808D6428D67248DDB8CA65DBC5978283"
KeyPath="yes"
Source="$(var.MenusPath)\ClientListView Lite.r5m"
<Condition>$(var.FLAVOUR)=LITE</Condition>
</Component>
Run Code Online (Sandbox Code Playgroud)
因此,如果 FLAVOR 为 LITE,上面的示例将安装一个名为“ClientListView Lite.r5m”的文件,如果 FLAVOR 为 200,它将安装一个名为“ClientListView 200.r5m”的文件。
这一切都按预期工作,并且已经做了多年!!
但是现在,我们有我们产品的网络版本,我们需要一个 zip 文件来包含将为每种口味安装的文件夹结构和文件。我发现您可以使用 MSIEXEC 和 /a 参数在命令行上运行 msi,然后将所有已安装到文件夹中的内容重定向并认为这正是我想要的......但可惜它不像我那样工作早就料到了。
它似乎正在做的是运行 MSI 并将文件提取到目标文件夹中,但它忽略了风味,因此您最终将“ClientListView Lite.r5m”和“ClientListView 200.r5m”文件都提取到文件夹; 这显然不是我想要的。
在阅读 MSIEXEC 上的文档后,您似乎可以传递公共属性的值,例如 msiexec.exe /a "C:\Example.msi" MY_PROP="myValue" - 所以我认为这可能对我有帮助;所以在我的 WiX 代码中,我添加了以下行:
<Property Id='PRODTYPE' Value="$(var.FLAVOUR)"/>
Run Code Online (Sandbox Code Playgroud)
然后将我的组件条件更改为:
<Component Id="cmp7F45920B1AA100729BAE37FC846B3FC5" Guid="*">
<File Id="fil238A776D9294E14671E012472F9F7196"
KeyPath="yes"
Source="$(var.MenusPath)\ClientListView 200.r5m"
<Condition><![CDATA[PRODTYPE=200]]></Condition>
</Component>
<Component Id="cmp8BFF42B232724DC4BA5B8F87994DEF21" Guid="*">
<File Id="fil808D6428D67248DDB8CA65DBC5978283"
KeyPath="yes"
Source="$(var.MenusPath)\ClientListView Lite.r5m"
<Condition><![CDATA[PRODTYPE=LITE]]></Condition>
</Component>
Run Code Online (Sandbox Code Playgroud)
但是虽然编译没问题,但通过以下方式运行它:
msiexec /a OurProduct.msi /qb PRODTYPE=200 TARGETDIR="C:\InstalledFiles200"
Run Code Online (Sandbox Code Playgroud)
仍然提取 200 和 LITE 口味的两个文件,我只想要一个 200。
那么,我是否正在尝试做一些不可能的事情......或者我做错了什么 - 任何帮助将不胜感激,因为在批处理文件中模仿该过程来创建我的 zip 的替代方法;会很可怕!!
干杯,
克里斯。
当我意识到你只想要一个 MSI(至少一次 - 看起来)时,我正在回答使用预处理器构造(?if?等)的建议,所以我跳过了它。我通常使用这样的结构来编译来自同一个 WiX 源的 MSI 文件。我已经抛弃了我在下面写的内容,并进行了一些重新整理 - 没有太多评论。我稍后会检查它。
在您的情况下,我可能会遗漏某些内容,但我看不到?if?语句在安装时如何工作 -它是一个编译时构造。它在编译和链接之前预处理您的 WiX 源文件。因此,听起来您实际上已经编译了三个不同版本的 MSI 文件,然后在每个版本上运行 admin install?如果是这种情况,则管理映像的使用无关紧要,因为您的整个 MSI 不包含任何内容,只包含您包含的组件?if?- 无需传入任何版本属性规范。
当我需要创建不同的设置风格或产品版本时,我使用的常规技术方法如下:
预处理器结构:用于在编译时编译MSI 的不同风格或版本(也包括不同的语言版本)。一种这样的结构就是?if?您提到的语句。还有其他几个,如上面直接提供的链接所示。与您相关的是条件语句(if,ifdef,ifndef,else,elseif,endif)和可能的include语句。
所以总体信息是预处理器构造允许您从单个 WiX XML 源构建多种不同风格的 MSI 文件。
在您的情况下,这将产生一个WiX XML 源和三个 MSI 文件- 每个文件用于不同版本的应用程序。一切都相似,但只有他们需要的组件。
通过/i或管理映像(文件提取)通过运行安装/a只会产生您添加到该安装风格的组件,但您没有像您说的那样具有单个 MSI - 但取决于您的编译方式,有三个不同的 MSI。
我更喜欢使用?include?语句来包含 WiX 片段文件,而不是?if?像你提到的那样直接使用条件组件。在“预处理器构造”部分的底部有一个差异示例。
MSI 功能(正如 PhilDW 回答的那样):您还可以依靠 MSI 功能来提供不同风格的单一设置。功能用于将 MSI 分隔为各种用户可选择的安装项目(您可以将其中的一些设为强制性)。
要安装的功能可以由用户调整或通过自定义操作以编程方式操作(例如,覆盖用户规范并自动执行技术设计 - 例如)。
用户功能控制
用户可以通过在大多数设置(不是全部)中找到的设置的 FeatureTree 对话框来控制要交互安装的功能。通常这涉及选择执行“自定义”安装。
您还可以通过使用ADDLOCAL 属性和REMOVE 属性(以及可能的其他类似属性 - 有关详细信息,请参阅链接)启动安装的命令行来指定要在安装程序中安装的功能。
有时,您可以在安装期间设置自定义属性,该属性会根据指定的“版本”触发要安装的一系列标准功能,这将我们带到下一点。
程序化功能控制
添加指向编程功能控制答案的链接:如何根据自定义操作中设置的属性安装功能?
如上所述,您可以在安装程序运行时使用ADDLOCAL 属性和安装程序中的REMOVE 属性,通过命令行操作要安装的功能。为此,您可以使用自定义操作。
还有一个与功能的安装状态相关的INSTALLLEVEL 属性。每个功能都有一个安装级别,它可能受条件表中设置的条件影响。
因此,条件表可用于基于条件表达式修改 Feature 表中任何条目的选择状态。
换句话说,您可以使用它来根据条件将功能设置为默认安装或不安装。
除了 INSTALLLEVEL 概念之外,您还可以使用自定义操作来控制特征选择状态。
例如,使用这些编程构造,您的设置可以根据用户输入的序列号更改要安装的功能选择状态。
您的设置还可以根据您运行的操作系统确定不应安装某个功能(Tablet OS例如,不安装功能)。
以编程方式更改功能选择可能有许多技术和实际原因。
需要注意的是,总体结果是可以通过命令行或用户设置功能选择,然后出于技术原因,您的设置会进行一些“幕后”修改。
只是指出:以编程方式操作安装的功能通常隐藏在视图中(但仍然可以通过命令行覆盖 - 这可能是一个问题)。
有关安装设置的功能、组件和自定义的更多信息,请点击此处:如何更好地利用 MSI 文件。
Setup.exe 启动器:一种以不同风格进行复杂部署的“现代”方法可以是使用 WiX 的刻录功能来编译安装在不同“集合”中的较小 MSI 设置,以产生不同的安装状态。
我更喜欢尽可能少的设置风格,但大多数时候我最终使用预处理器构造为不同的语言版本(英语、德语、俄语)以及不同的产品版本(企业版、专业版、标准版)创建不同的设置. 我通常将它们设置为所有相关的共享升级代码,并且无法并行安装。
我觉得单个 MSI 获得更多 QA 资源,因此会得到更好的测试。但是,如果设置非常大,这种单一的 MSI 方法通常会崩溃 - 在这种情况下,我喜欢拆分它们。我还喜欢为这里解释的商业和现实世界的实际原因对每种语言进行单独的设置。实际和法律要求(许可)也可能使编译相同 MSI 设置的特殊版本成为必要。我还被要求为OEM 供应商构建新的 MSI 风格。
现在有一个令人惊讶的结论(你从未要求过 :-) ):综上所述,在理想的世界中,我喜欢安装所有功能而无需在设置中进行任何技术大惊小怪,并使用序列号来确定应用程序的功能和应在应用程序中激活模块。我还喜欢将序列密钥验证放在应用程序中,而不是在设置中。原因是什么?我希望设置尽可能原始,以确保正确安装应用程序,而不会出现高部署错误百分比。因此,我在部署中寻找复杂性以确保减少部署支持问题。 可靠的部署是产品成功的关键. 安装后,您的应用程序可以在更可调试和可预测的上下文中启动(没有条件、模拟或排序问题),并以交互方式向用户有意义地报告任何错误 - 他们可以致电支持,更有希望成功解决任何问题如果安装程序被一个神秘的非交互式错误消息(在系统的事件日志中)炸毁,情况会怎样。
一旦你得到一个好的、简单的部署解决方案,你作为发布经理和设置开发人员的工作应该扩展到处理应用程序的启动顺序、文件配置和整体配置,以及在我看来你可能想要添加到的支持和调试功能应用程序(应用程序的自检以及日志记录和自动收集信息以发送给支持人员)。尝试扩大您对应用程序本身的责任和影响,以保持部署“尽可能原始”。从长远来看,任何可以在应用程序中而不是在设置中编码的东西都会更可靠(并且更容易调试)。
回到现实:对于真正的部署,您将通过缓慢的网络共享获得一堆带有不规则版本控制方案的文件,其中包含来自离岸并按小时支付的开发人员的一堆不连贯的指令(您可以确定他感受到了痛苦)也 :-) )。而且他们会对你的设置有一些部分疯狂的要求来做魔法来掩盖他们不太理想的设计(这是他们得到的全部报酬)。这不是夸大其词,它是现实-真难(好吧,这是在胡说八道)。我们必须努力改进应用程序的整体设计,使部署更不容易出错。魔术确实可以在设置中完成,但由于部署的不可避免和不守规矩的复杂性(最重要的是:1)错误是累积的——每个版本都会增加风险和机会来破坏你的安装的可维护性(有时你必须把所有东西都拉回来重新开始——代价很大),2)目标系统处于极其多样化和不可预测的状态(任何状态,任何语言,任何操作系统版本、任何硬件、任何恶意软件等...),3)当您无法以交互方式访问问题系统时,调试非常困难 - 有关更多详细信息,请参阅链接答案的底部。我将添加一个4th issue与第二个不同的问题:你能保证部署在充满恶意软件的机器上吗?这样的系统是不可能支持和调试的——只要选择找到一个你无法修复的关键问题。如果您的程序包在此类恶意软件系统上失败,这是不可避免的自然错误百分比。
总体观点很明显:我们无法解释这些未知因素 - 垃圾输入,垃圾输出(对于这听起来如何表示歉意) - 每次部署时都会有一个错误百分比 - 即使是完美的包。确保抓住所有可用且始终有价值的 QA 人员并教他们安装测试(测试所有安装模式、测试卸载和与其他应用程序的交互、测试实际升级场景、测试特定的高级设置功能,例如许可证检查等...)。获得您能获得的所有帮助并重视他们的贡献。如果不出意外,作为同伙:-)。说真的:未能有效利用可用的 QA 资源并帮助在部署测试中培训他们是我的清单上的重要内容,因为我作为发布经理和设置开发人员最大的疏忽。可以肯定的是,您很快就会欠您的 QA 人员一瓶 Horílka - 或绿茶或 Rooibos 茶 - 取决于您在世界上的哪个位置:-)。
我可能会使用预处理器构造 ?if?并?include?根据您?define?在示例顶部的语句中定义的版本有选择地包含 WiX XML 源代码/标记的不同部分:
<?define EditionType = “LITE” ?>
<!-- You can put your edition-specific components in an include file -->
<?if $(var.EditionType) = "200" ?>
<?include "200Features.wxi" ?>
<?endif ?>
<?if $(var.EditionType) = "LITE" ?>
<?include "LiteFeatures.wxi" ?>
<?endif ?>
Run Code Online (Sandbox Code Playgroud)
除了使用包含文件,您还可以在主源中内联执行此操作(这可能是您所做的):
<?if $(var.EditionType) = "200" ?>
<Component>
<File Source="$(var.MenusPath)\ClientListView 200.r5m" />
</Component>
<?endif?>
<?if $(var.EditionType) = "LITE" ?>
<Component>
<File Source="$(var.MenusPath)\ClientListView Lite.r5m" />
</Component>
<?endif?>
Run Code Online (Sandbox Code Playgroud)
在上述选项中,我更愿意将特定于版本的组件保留在单独的包含文件中(第一个选项) - 这样您的 WiX 源中的“行噪声”就会更少(预处理器结构的重复次数更少)。
包含文件基本上只是 WiX XML 文件,它们就像 C++ 头文件一样在编译时包含到父 WiX XML 文件中。就像 Phil 说的,我会为不同的版本使用不同的功能名称,因此您200Features在 200Features.wxi 包含文件中定义了一个功能,但您不需要使用功能 - 尽管强烈建议。
还要注意片段元素的 WiX 概念。本质上是一种交叉引用 WiX 源文件中内容的方法。这里有一个例子。
我应该提到MSI中合并模块的原始概念提供了一种在不同设置之间共享组件的不同方式,但我更喜欢包含文件方法。合并模块在某种程度上感觉像是 COM 对象(二进制 blob 作为一个版本化的整体包含在内),而包含文件似乎更具动态性,因为您可以获得链接到每个构建的最新文件,而无需任何合并模块重建和版本控制。有些人无疑会觉得这是非常错误的。
| 归档时间: |
|
| 查看次数: |
3223 次 |
| 最近记录: |