【问题标题】:Upgrade individual feature in WIX feature-tree without uninstalling/upgrading other feature(s)升级 WIX 功能树中的单个功能而不卸载/升级其他功能
【发布时间】:2010-12-17 04:15:43
【问题描述】:

我正在尝试使用WIX 创建一个安装项目,这将允许我安装单个产品的多个功能。如何更新已安装的功能之一(独立于其他已安装的功能)而无需重新安装功能树中的其他功能?

例如,我希望能够有一个名为 HelloWolrd 的项目(回到 HelloWolrd),它(惊喜)打印“Hello world!”屏幕上。假设我有三个 hello world 应用程序,Hello World 1、Hello World 2 和 Hello World 3。每个应用程序都分别在屏幕上打印 Hello World 1、2 或 3。我想要的是创建一个 MSI,它默认安装所有这三个“功能”,但也允许以后单独升级每个功能。

这是我的解决方案布局:

Solution Explorer http://img12.imageshack.us/img12/5671/solutionexplorerm.jpg

我的 WIX Product.wxs 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="ca484210-c719-4b2e-b960-45212d407c11" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0" Manufacturer="HelloWorldInstaller" UpgradeCode="68eeb8cb-9ef3-443c-870c-9b406129f7ff">
        <Package InstallerVersion="200" Compressed="yes" />

        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

        <!-- Create Directory Structure -->
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLLOCATION" Name="Hello World" />
            </Directory>
            <Directory Id="DesktopFolder" Name="Desktop"/>
        </Directory>

        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="HelloWorld1" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld1\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld1.exe" Name="$(var.HelloWorld1.TargetFileName)" Source="$(var.HelloWorld1.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld1ApplicationDesktopShortcut" Name="Hello World 1" Description="Hello World Application 1" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>

            </Component>
            <Component Id="HelloWorld2" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld2\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld2.exe" Name="$(var.HelloWorld2.TargetFileName)" Source="$(var.HelloWorld2.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld2ApplicationDesktopShortcut" Name="Hello World 2" Description="Hello World Application 2" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>
            </Component>
            <Component Id="HelloWorld3" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
                <RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld3\Install" Action="createAndRemoveOnUninstall">
                    <RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
                </RegistryKey>

                <File Id="HelloWorld3.exe" Name="$(var.HelloWorld3.TargetFileName)" Source="$(var.HelloWorld3.TargetPath)" DiskId="1" Checksum="yes">
                    <Shortcut Id="HelloWorld3ApplicationDesktopShortcut" Name="Hello World 3" Description="Hello World Application 3" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
                </File>
            </Component>
        </DirectoryRef>

        <Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
            <ComponentRef Id="HelloWorld1"/>
        </Feature>
        <Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
            <ComponentRef Id="HelloWorld2"/>
        </Feature>
        <Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
            <ComponentRef Id="HelloWorld3"/>
        </Feature>

    </Product>
</Wix>

现在,当它构建完成时,它会按照您的预期安装功能。但是,当您对 HelloWorld1.vb 进行修改并重新编译时,我希望它能够仅重新安装(升级)该功能,而不是全部。

当我更新一个文件并重建解决方案,然后尝试安装 msi 时,我收到此错误:

MSI Error http://img696.imageshack.us/img696/849/anotherversionisinstall.jpg

我更新了我的代码以允许卸载功能并允许使用升级代码,但是卸载了所有功能并重新安装了所有功能。


-- 现实世界的应用 --

对此的实际应用程序是一个大型软件包,需要多个支持应用程序作为服务/计划任务定期运行。我想将这些支持应用程序安装到一个 MSI 中,这样我们就不会像单独推出每个 exe 那样做噩梦了。我知道,如果我们对其中一个 exe 进行了更新,我们可以手动编译该 exe 并将其推出,但我想以完全可重现的方式执行此操作。

如有任何帮助,将不胜感激,

谢谢!

编辑:

我添加了来自Google Code 的下载源。再次感谢!

【问题讨论】:

  • 为什么避免重新安装功能很重要?最后的结果不一样吗?我认为除了查看安装日志之外没有其他方法可以区分。
  • @wcoenen - 我想避免重新安装其他功能的原因是我们在 FeatureA 中发现了一个错误,需要修复它。如果 bug 不影响 FeatureB 或 FeatureC,则不需要停止它们的处理来修复 FeatureA。
  • @Scott 我知道你发布这个已经有几年了,但我很好奇你是否还有可用的示例代码?我查看了上面指向谷歌代码的链接,但在列表中没有看到这个问题编号。谢谢。
  • @Adam - 我可能有它,我会四处看看,看看我是否不能为你挖掘它。如果我找到它,我会在这里发表评论。谢谢!

标签: wix windows-installer setup-project wix3 feature-upgrade


【解决方案1】:

我想通了,并认为我会在此处发布答案以供其他人将来参考。所以我已经充分解释了这个问题,我将更深入地研究现实世界的场景。

我们有一个中等大小的软件,它要求我们有多个支持应用程序,在多个不同的服务器上运行。我们当前的升级进程使得以可靠的方式升级代码变得相当困难。目前我们使用自解压 exe 将我们的代码部署到不同的服务器。当我们拥有如此大量的支持应用程序时,就会出现问题,以至于很难确保应用程序使用正确的配置设置等正确安装。为了解决这个问题,我们正在研究能够代替压缩每个应用程序的能力对于支持应用程序,我们创建了一个安装程序 (MSI),允许基础架构团队将一组特定的支持应用程序安装到每台给定的机器上。当我们有重大更改(例如从 1.0 到 2.0)时,我们将进行完整升级安装(这意味着所有服务/进程都需要停止、卸载、安装和启动。)当我们有微小更改时,我们希望只需要停止并重新安装受影响的服务/进程,而无需触及其他应用程序。现在,我已经够啰嗦了,让我们来看看解决方案:

我修改了 WIX Product.wxs 以删除快捷方式,因为我们在场景中并不真正需要它们。这是更新的 wxs 文件:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Product Id="13C373D3-5C27-487e-A020-C2C89E4607B1" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0"
      Manufacturer="HelloWorldInstaller" UpgradeCode="E7CB3C76-4D51-48a8-BFB4-6D11B2E2E65B">

  <Package InstallerVersion="200" Compressed="yes" />

  <Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
  <FeatureRef Id="HelloWorld1Feature" />
  <FeatureRef Id="HelloWorld2Feature" />
  <FeatureRef Id="HelloWorld3Feature" />
 </Product>

 <Fragment>
  <Directory Id="TARGETDIR" Name="SourceDir">
   <Directory Id="ProgramFilesFolder">
    <Directory Id="INSTALLLOCATION" Name="Hello World" />
   </Directory>
   <Directory Id="DesktopFolder" Name="Desktop"/>
  </Directory>
 </Fragment>

 <Fragment>
  <DirectoryRef Id="INSTALLLOCATION">
   <Directory Id="HelloWorld1Directory" Name="Hello World 1">
    <Component Id="HelloWorld1Component" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
     <File Id="HelloWorld1.exe" Name="HelloWorld1.exe" Source="HelloWorld1.exe" DiskId="1" Checksum="yes" />    
    </Component>
   </Directory>
   <Directory Id="HelloWorld2Directory" Name="Hello World 2">
    <Component Id="HelloWorld2Component" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
     <File Id="HelloWorld2.exe" Name="HelloWorld2.exe" Source="HelloWorld2.exe" DiskId="1" Checksum="yes" />
    </Component>
   </Directory>
   <Directory Id="HelloWorld3Directory" Name="Hello World 3">
    <Component Id="HelloWorld3Component" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
     <File Id="HelloWorld3.exe" Name="HelloWorld3.exe" Source="HelloWorld3.exe" DiskId="1" Checksum="yes" />
    </Component>
   </Directory>
  </DirectoryRef>
 </Fragment>

 <Fragment>
  <Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
   <ComponentRef Id="HelloWorld1Component"/>
  </Feature>
 </Fragment>
 <Fragment>
  <Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
   <ComponentRef Id="HelloWorld2Component"/>
  </Feature>
 </Fragment>
 <Fragment>
  <Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
   <ComponentRef Id="HelloWorld3Component"/>
  </Feature>
 </Fragment>
</Wix>

现在,对于我们的小升级,我们将考虑为我们的组件发布补丁。

例如,假设我们有一个 ProductA,它具有三个组件 - 1、2 和 3。这三个组件必须作为服务或计划任务运行。我们产品的性质,我们不能关闭所有服务来更新或修复我们的组件之一。因此,如果在我们安装了 1.0 版之后,我们在组件 2 中发现了一个错误,但我们不希望 1 或 3 受到针对此错误的修复的影响,我们将为组件 2 发布一个补丁,因此只有组件 2 会受到影响。

对于上面的快速示例,我们使用 HelloWorld1、HelloWorld2 和 HelloWorld3 作为我们软件应用程序中的 3 个组件。我们的想法是,我们应该能够使用一个 MSI 安装所有三个,然后独立更新每个,而不会影响任何其他已安装的组件。

因此,为了演示这一点,我创建了上面的三个控制台应用程序,它们将显示“Hello World 1!”、“Hello World 2!”和“Hello World 3!”。然后在我们发布初始 MSI 之后,假设我们发现了一个“错误”,要求我们让 HelloWorld1 说“Hello World 1!更新”。反而。以下是我们将要做的模拟:

  1. 通过在命令提示符处执行以下命令来创建 Product.wixobj:
    candle.exe Product.wxs
    请记住,为了调用 candle.exe 或任何 WIX 命令,Wix 安装目录应该是在您的 PATH 变量中。 (Short tutorial on updating PATH environment variable) 另外,请在与您的 Product.wxs 文件相同的目录中执行命令。
  2. 创建产品的第一个版本(比如说 1.0):
    light.exe Product.wixobj -out ProductA-1.0.msi
  3. 现在找到一个错误(将 HelloWorld1 的输出更改为“Hello World 1!已更新。”)然后更新程序集版本和文件版本。这很重要,因为这是 WIX 可以告诉 exe 不同的方式。
  4. 运行与第一步相同的命令(为了更好的衡量标准):
    candle.exe Product.wxs
  5. 运行与第二步几乎相同的命令:
    light.exe Product.wixobj -out ProductA-1.1.msi
    请注意,这是 1.1 版而不是 1.0 版(这是我们更新代码的 msi)。但是,我们不想只安装它,继续阅读。
  6. 这是有趣的部分,我们可以通过以下命令了解两个 MSI 的差异:
    torch.exe -p -xi ProductA-1.0.wixpdb ProductA-1.1.wixpdb -out Diff.WixMst
  7. 现在我们从这里创建补丁文件(Patch.wxs 将在下面解释):
    candle.exe Patch.wxs
  8. 我们现在将使用以下命令创建 WixMsp 文件:
    light.exe Patch.wixobj -out Patch.WixMsp
  9. 现在,有趣的部分。使用以下命令创建 MSP 文件:
    pyro.exe Patch.WixMsp -out Patch.msp -t RTM Diff.Wixmst

现在,如果一切按计划进行,您应该有两个 msi 和一个 msp 文件。如果您安装第一个 msi (ProductA-1.0.msi) 并运行 HelloWorld1.exe,您应该会看到消息“Hello World 1!”。只是为了好玩(和示例),运行其他两个应用程序并让它们运行(我内置了一个停止以保持它们打开)。关闭 HelloWorld1.exe,因为我们现在要为该 exe 应用更新,但这样做不会影响 HelloWorld2.exe 或 HelloWorld3.exe。如果您现在安装 msp (Patch.msp) 文件,然后运行 ​​HelloWorld1.exe,您将看到更新的消息“Hello World 1!已更新”。

现在,对于 magical Patch.wxs 文件:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Patch
   AllowRemoval="yes"
   Manufacturer="Dynamo Corp"
   MoreInfoURL="http://www.dynamocorp.com/"
   DisplayName="Sample Patch"
   Description="Small Update Patch"
   Classification="Update"
        >

  <Media Id="5000" Cabinet="RTM.cab">
   <PatchBaseline Id="RTM"/>
  </Media>

  <PatchFamilyRef Id="SamplePatchFamily"/>
 </Patch>

 <Fragment>
  <PatchFamily Id='SamplePatchFamily' Version='1.0.0' Supersede='yes'>
   <ComponentRef Id="HelloWorld1Component"/>
  </PatchFamily>
 </Fragment>
</Wix>

看起来不多,是吗?嗯,最有趣的部分是这些:

  1. &lt;PatchBaseline Id="RTM"/&gt; - 如果您还记得的话,这用于我们创建补丁 msi。 “RTM”在上面的最后一步中引用:-t RTM - 这些必须匹配。
  2. &lt;ComponentRef Id="HelloWorld1Component"/&gt; - 这会将补丁指向要更新的正确组件,在我们的例子中是 HelloWorld1Component。

如果您一直在搜索,上面的代码可能看起来很熟悉,因为它来自Peter Marcu's BlogWiX: Building a Patch using the new Patch Building System - Part 3

我也非常依赖Alex Shevchuk's Blog:From MSI to WiX, Part 8 - Major Upgrade

如果您想知道,“哇,有很多步骤,为什么有人会做这么多步骤?”,请记住,一旦完成(以上)艰苦的工作,您需要将其移至您的集成例程中.没错,整合,整合,整合!你怎么做到这一点?好吧,那是更多的研究,也许是一篇博客文章? - 大概。为了让你走上正轨,这里有一篇关于Automate Releases With MSBuild And Windows Installer XML 的精彩文章。

哇,我希望你们(你们两个)都阅读了所有这些内容,并且学到了很多东西。我希望这对我以外的人有所帮助。

谢谢!

【讨论】:

  • 这很有趣,我正在为您的答案添加书签以供将来参考。你能把它清理一下吗?您重复了问题本身已经存在的大量信息。通过删除此处的冗余并将其重新表述为真正相关的答案,它会更容易阅读。这对我来说非常混乱和乏味,因为我刚刚阅读了您的问题,然后不得不再次重新阅读所有上下文信息(出现在问题中)。
  • 伟大的现实世界的例子。要添加的一件事-torch.exe 在我的机器上总是失败,错误代码为TRCH0001,这似乎是workaround 的一个已知错误。此错误已在 Wix v4 中修复。
【解决方案2】:

听起来你想出了升级方案,现在你只需要想出Where to place RemoveExistingProducts in a major MSI upgrade,这样如果功能没有改变就不会重新安装:)

【讨论】:

  • 我认为这实际上并不能回答我的问题,因为它仍然会在升级过程中删除我的功能。不过,我找到了解决方案,很快就会发布。
猜你喜欢
  • 2017-10-01
  • 1970-01-01
  • 2018-04-26
  • 1970-01-01
  • 2019-12-17
  • 2016-09-08
  • 2021-07-11
  • 1970-01-01
  • 2015-05-24
相关资源
最近更新 更多