代码之家  ›  专栏  ›  技术社区  ›  Big Chris

Wix安装程序:在命令行执行MSIEXEC管理安装时设置组件条件属性

  •  2
  • Big Chris  · 技术社区  · 7 年前

    我们有三种类型/口味的产品,但只有一种是用WiX编写的MSI。当我们构建安装程序时,我们通过一个定义的常量传入味道:

    Call MSBUILD.bat ..\MSIs\CoreProduct\OurProduct.sln /p:DefineConstants="FLAVOUR=%_Flavour%"
    

    常量在Visual Studio中的“构建”下设置->将预处理器变量定义为Flavor=50。构建过程中,传入值50、200或LITE作为味道。

    在WiX代码中,我们的组件上有大量的条件,根据味道告诉它要安装哪个文件;eg公司

          <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>
    

    因此,如果Flavor为Lite,上面的示例将安装一个名为“ClientListView Lite.r5m”的文件;如果Flavor为200,则将安装一个名为“ClientListView 200.r5m”的文件。

    这一切都像预期的那样工作,并且已经做了多年!!

    但现在,我们的产品有了一个web版本,我们需要一个zip文件来包含文件夹结构和将为每种口味安装的文件。我发现您可以使用MSIEXEC和/a参数在命令行上运行msi,然后将已安装的所有内容重定向到文件夹&我想这正是我想要的。。。但遗憾的是,它并没有像我预期的那样起作用。

    它似乎在运行MSI并将文件提取到目标文件夹中,但它忽略了味道,因此最终将“ClientListView Lite.r5m”和“ClientListView 200.r5m”文件都提取到了该文件夹中;这显然不是我想要的。

    在阅读MSIEXEC上的文档时,似乎可以传入公共属性的值,例如MSIEXEC。exe/a“C:\Example.msi”MY\u PROP=“myValue”-所以我认为这可能会对我有所帮助;因此,在我的WiX代码中,我添加了一行:

        <Property Id='PRODTYPE' Value="$(var.FLAVOUR)"/>
    

    然后将我的组件条件更改为:

      <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>
    

    但尽管编译正常,但通过以下方式运行它:

    msiexec /a OurProduct.msi /qb PRODTYPE=200 TARGETDIR="C:\InstalledFiles200"
    

    仍提取200和;LITE口味,我只想要一个200美元的。

    那么,我是不是在尝试做一些不可能的事情。。。还是我做错了什么事?如果能提供任何帮助,我们将不胜感激,因为可以在批处理文件中模拟该过程来创建我的zip;将是可怕的!!

    干杯

    克里斯。

    3 回复  |  直到 7 年前
        1
  •  5
  •   Stein Åsmul    7 年前

    您的预处理器使用

    我半途而废,建议使用 预处理器构造 ( ?if? 等)当我意识到你只想要一个MSI(至少一次-似乎),所以我跳过了它。我通常使用这种构造来编译来自同一WiX源的不同风格的MSI文件。我把我在下面写的内容重新整理了一下——没有太多的评论。我稍后会查看它。

    在你的情况下,我可能遗漏了一些东西,但我不知道 ?如果 语句可以在安装时工作- 它是一个编译时构造 。它在编译和链接之前预处理WiX源文件。像这样的 听起来您实际上已经编译了三个不同版本的MSI文件,然后在每个MSI文件上运行admin install? 如果是这种情况,那么管理映像的使用就无关紧要了,因为您的整个MSI除了包含的组件之外不包含任何内容 ?如果 -不需要传入任何版本属性规范。


    设置风格

    当我需要创建不同的 设置风格 产品版本 具体如下:

    1. Preprocessor constructs : 用于在以下位置编译MSI的不同风格或版本(以及不同的语言版本) 编译时间 。一个这样的构造是 ?如果 您提到的声明。正如上面提供的链接所示,还有其他几个问题。与您相关的是 条件语句 ( if , ifdef , ifndef , else , elseif , endif )可能还有 include 陈述

      • 所以总体信息是 预处理器构造允许您从单个WiX XML源构建几种不同风格的MSI文件

      • 在您的情况下,这将产生 单个WiX XML源 三个MSI文件 -每一个都适用于不同版本的应用程序。都很相似,但只有他们需要的组件。

      • 通过运行安装 /i 或管理映像(文件提取)通过 /a 只会生成您添加到该设置风格的组件,但您没有您所说的单个MSI,而是有三个不同的MSI,具体取决于您的编译方式。

      • 我更喜欢使用 ?include? 语句包括WiX片段文件,而不仅仅是直接使用 ?如果 正如你所说。“中的底部有一个差异示例” 预处理器构造 “第节。

    2. MSI功能 (正如菲尔德回答的那样) : 您还可以依靠MSI功能提供不同风格的单一设置。这些功能用于将MSI分隔为各种用户可选择的安装项目(其中一些是必须的)。

      • 功能允许以不同的风格安装单个MSI -同时含有每种口味所需的所有成分。调节组件可以达到类似的效果。
      • 调节部件 类似于特征操纵,但在更原始和基本的级别上工作。这些不是用户可选择的安装单元,而是要安装并隐藏在用户视线之外的产品的基本和原子位和部件,而功能是用户可选择的,用户通常可以看到。
      • 要安装哪些功能 可以是 由用户调整 以编程方式操纵 通过自定义操作(例如,覆盖用户规范并自动执行技术设计)。

        • 用户功能控件

          • 用户可以通过大多数设置(并非全部)中的设置的FeatureTree对话框控制功能以交互方式安装。通常这涉及到选择进行“自定义”安装。

          • 您还可以通过启动安装的命令行,使用 ADDLOCAL property 以及 REMOVE property (以及潜在的其他类似属性-有关详细信息,请参阅链接)。

          • 有时,您可以在安装过程中设置一个自定义属性,该属性将根据指定的“版本”触发一系列标准功能的安装,这就引出了下一点。

        • 编程功能控件

          • 添加指向编程功能控制答案的链接: How to install feature based on the property set in custom action?

          • 如上所述,您可以使用 ADDLOCAL属性 以及 删除属性 在安装程序运行时在其内部。 为此,可以使用自定义操作

          • 还有一个 INSTALLLEVEL property 这与功能的安装状态相关。对于每个功能,都有一个安装级别,它可能会受到中设置的条件的影响 Condition table

            • 因此,可以使用条件表根据条件表达式修改特征表中任何条目的选择状态。

            • 换言之,您可以根据条件将功能设置为默认安装或不安装。

          • 除了INSTALLLEVEL概念之外,您还可以使用 自定义操作 控制特征选择状态。

            • 今天,这比功能条件更加可靠,因为许多应用程序打包程序通常会最大限度地使用INSTALLLEVEL属性来强制安装所有功能。 这是错误的,因为如果某些功能与您运行的操作系统不兼容,则可能不需要安装这些功能 。我试着把这件事告诉许多聋子。
          • 例如,使用这些编程构造,安装程序可以根据 串行密钥 由用户输入。

          • 您的安装程序还可以根据 操作系统 您正在运行(不安装 Tablet OS 例如,功能)。

          • 以编程方式更改特性选择可能有许多技术和实际原因。

      • 需要注意的是,总体结果是可以通过命令行或用户设置功能选择,然后您的设置会出于技术原因进行一些“隐藏”修改。

      • 需要指出的是:以编程方式操作以进行安装的功能通常是隐藏的(但仍然可以通过命令行进行覆盖,这可能是一个问题)。

      • 有关安装的功能、组件和自定义设置的更多信息,请参见此处: How to make better use of MSI files

    3. 设置。exe启动器 :以不同风格进行复杂部署的“现代”方法可以是使用WiX的Burn功能编译安装在不同“集合”中的较小MSI设置,以产生不同的安装状态。

      • 我发现这对于一般使用来说太复杂了,但这当然是可能的。我想有些人会觉得更容易,因为功能操作更少。也许我只是缺乏这方面的经验。
      • 好处是MSI文件更小,安装速度更快,您可以更新单个MSI文件并创建新的安装程序。exe包装,然后对整个解决方案执行全面QA,而无需重建所有设置。
      • 在我的世界里,一个单一的、更新的MSI无论如何都需要一个完整的QA,所以我自己并不总是买这些“简单性论证”。 每个发布周期都有风险,因此会增加总风险 。不过,能够重建一个小的设置并保持您的大设置稳定,这可能会很好。

    实际使用情况

    我更喜欢尽可能少的设置风格 ,但大多数时候,我最终使用预处理器构造为 不同语言版本 (英语、德语、俄语)以及 不同的产品版本 (企业、专业、标准)。我通常将它们都设置为共享一个升级代码,而不能并排安装。

    我觉得单个MSI可以获得更多的QA资源,因此可以更好地进行测试。 然而,如果设置非常大,这种单一的MSI方法通常会崩溃——在这种情况下,我喜欢将它们拆分。我还喜欢为每种语言分别设置 for business- and real-world practical reasons explained here 。实践和法律要求(许可)也使得必须编译同一MSI设置的特殊版本。我还被要求为 OEM供应商

    现在有一个令人惊讶的结论 (您从未要求:-):综上所述,在一个理想的世界中,我喜欢安装所有功能,而不需要任何技术上的麻烦,并使用串行键来确定应用程序中应该激活哪些应用程序功能和模块。我还喜欢将串行密钥验证放在应用程序中,而不是放在设置中。 原因 ? 我希望设置尽可能原始,以确保应用程序正确安装,而不会出现高部署错误百分比。因此,我寻求在部署中消除复杂性,以确保减少部署支持问题。 可靠的部署对产品的成功至关重要 。安装后,您的应用程序可以在更可调试和可预测的环境中启动(无条件、模拟或排序问题),并以交互方式和有意义的方式向用户报告任何错误,而且他们可以致电支持部门,希望成功解决任何问题,而不是安装程序突然出现神秘的非交互错误消息(在系统的事件日志中)。

    一旦您获得了一个好的、简单的部署解决方案,您作为发布经理和安装开发人员的工作应该扩展到处理应用程序的启动序列,文件配置和总体配置,以及我认为您可能要添加到应用程序中的支持和调试功能(应用程序自检、日志记录和自动收集要发送给支持人员的信息)。尝试扩展您对应用程序本身的责任和影响力,以保持部署“尽可能原始”。从长远来看,任何可以在应用程序中而不是在设置中进行编码的内容都将更加可靠(并且更容易调试)。

    回到现实中 :对于真正的部署,您将通过缓慢的网络共享获得一堆带有不规则版本控制方案的文件,其中包含一堆不连贯的指令,这些指令来自离岸开发人员,按小时付费(您可以肯定他也感到痛苦:-))。他们会对你的设置提出一些部分疯狂的要求,要求你施展魔法来掩盖他们不太理想的设计(这是他们花钱制作的)。这不是咆哮,这是现实——很遗憾地说( 好吧,这是一个咆哮 )。我们必须努力改进应用程序的总体设计,以使部署更不容易出错。魔法确实可以在设置中完成,但它会触发更高的部署错误百分比,因为 the unavoidable and unruly complexity of deployment (最重要的是: 1) 错误是累积的-每个版本都会增加风险和机会,从而破坏安装的可维护性(有时您必须收回所有内容并重新开始-代价高昂), 2) 目标系统处于极其多样化和不可预测的状态(任何状态、任何语言、任何操作系统版本、任何硬件、任何恶意软件等), 3) 当您无法以交互方式访问问题系统时,调试非常困难-有关更多详细信息,请参阅链接答案的底部。我将添加一个 4th issue 从第二个掉出来: 你能保证在充满恶意软件的机器上部署吗? 这样的系统是不可能支持和调试的——只需选择一个您无法修复的关键问题即可。 如果您的软件包在此类恶意软件系统上出现故障,那么这是不可避免的自然错误百分比

    总的观点是显而易见的: 我们无法解释这些未知因素——垃圾输入、垃圾输出(听起来很抱歉)——每次部署时都会有一个错误百分比,即使对于完美的包也是如此 。确保抓住所有可用的 总是有价值的QA人员 并教他们安装测试(测试所有安装模式,测试卸载和与其他应用程序的交互,测试真实世界的升级场景,测试特定的高级安装功能,如许可证检查等)。获得你能得到的所有帮助,并重视他们的贡献。如果没有其他,作为共犯:-)。严肃地说:作为发布经理和安装开发人员,我最大的疏忽就是未能有效地利用可用的QA资源并帮助培训他们进行部署测试。这是一个安全的假设,你很快就会欠你的QA人员一瓶霍尔卡,或者绿茶或Rooibos茶,这取决于你在世界上的位置:-)。


    预处理器构造

    我可能会使用 预处理器构造 ?如果 ?包括 根据您在中定义的版本,有选择地包括WiX XML源代码/标记的不同部分 ?define? 示例顶部的语句:

    <?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 ?>
    

    除了使用包含文件,您还可以在主源代码中内联执行此操作(这可能就是您所做的):

     <?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?>
    

    在上述选项中,我更愿意将特定于版本的组件保存在一个单独的包含文件中(第一个选项),这样WiX源中的“线噪声”就会更少(预处理器构造的重复次数更少)。

    include文件基本上就是WiX-XML文件,在编译时就像C++头文件一样被包含到父WiX-XML文件中。 正如Phil所说,我会为不同的版本使用不同的功能名称,所以您可以定义一个功能 200Features 在200Features中。wxi包括文件,但您不需要使用功能-尽管强烈建议使用。

    还要注意WiX的概念 the fragment element 。本质上是一种交叉引用WiX源文件中内容的方法。 An example here

    我应该提到的是 merge modules 在MSI中,提供了一种在不同设置之间共享组件的不同方法,但我更喜欢包含文件的方法。合并模块在某种程度上感觉像COM对象(二进制blob作为一个版本化的整体包含),而包含文件似乎更具动态性,因为您可以为每个构建获取链接到的最新文件,而无需任何合并模块重建和版本化。毫无疑问,有些人会发现这是非常错误的。

        2
  •  3
  •   PhilDW    7 年前

    msiexec命令行中的/a开关不是通常意义上的安装。它实际上只是将文件解包到某个位置。如果要进行实际安装,必须使用/i或双击MSI文件。这将为您提供一个正确的完整安装,并在Programs&功能等。

    安装内容的选择通常通过将安装程序划分为多个功能来处理,每个功能都包含一组包含所需功能的组件。在WiX UI中,您可以获得一个对话框来选择功能,维护模式将允许您返回并更改它们。在命令行安装中,您只需说/i[msi file]ADDLOCAL=Feature1、Feature2等等。如果您真的想使用“flavor”,那么在内部您可以将其转换为ADDLOCAL列表。

        3
  •  0
  •   Big Chris    7 年前

    谢谢你提供的信息,但我想你没有理解我想要实现的目标;我不想通过/I运行安装程序,因为正如您所说,这实际上会安装文件并更改注册表等。我想做的是运行安装程序两次,一次是针对Flavor 200,然后再次针对Flavor Lite;因此,如果我用/I运行它,我最终会得到两个包含“将”安装的文件的文件夹。显然,我不能用/I运行两次,因为第二次运行它时,它会卸载第一个。

    无论如何,我发现在管理员“安装”期间忽略了组件内的条件,而?如果语句不会被忽略-不知道为什么会这样,但我现在已经删除了所有条件语句,并将它们替换为IF语句-所以现在我可以做我想做的事情-使用/a运行两次,每个文件夹都将包含该风格特有的文件。