如何实现 WiX 安装程序升级?
Posted
技术标签:
【中文标题】如何实现 WiX 安装程序升级?【英文标题】:How to implement WiX installer upgrade? 【发布时间】:2010-09-11 23:10:49 【问题描述】:在工作中,我们使用WiX 来构建安装包。我们希望产品 X 的安装会导致在该机器上卸载该产品的先前版本。
我在 Internet 上的多个地方阅读了有关重大升级的信息,但无法使其正常工作。 谁能指定将卸载以前版本功能添加到 WiX 需要采取的确切步骤?
【问题讨论】:
【参考方案1】:您最好在WiX-users mailing list 上问这个问题。
最好在充分了解 Windows Installer 正在做什么的情况下使用 WiX。您可以考虑获取“The Definitive Guide to Windows Installer”。
删除现有产品的操作是RemoveExistingProducts action。因为它所做的事情的后果取决于它的计划位置 - 即故障是否导致旧产品重新安装,以及是否再次复制未更改的文件 - 您必须自己安排。
RemoveExistingProducts
处理当前安装中的<Upgrade>
元素,将@Id
属性与系统上所有已安装产品的UpgradeCode
(在<Product>
元素中指定)相匹配。 UpgradeCode
定义了一系列相关产品。任何具有此 UpgradeCode、版本在指定范围内且UpgradeVersion/@OnlyDetect
属性为no
(或被省略)的产品都将被删除。
RemoveExistingProducts
的文档提到设置 UPGRADINGPRODUCTCODE
属性。这意味着正在删除的产品的卸载进程接收该属性,其值为正在安装的产品的Product/@Id
。
如果您的原始安装不包含UpgradeCode
,您将无法使用此功能。
【讨论】:
毫无疑问,Mike 确切地知道他在说什么,恕我直言,但当我考虑用对 Windows 安装程序在做什么的坚定理解来思考混乱我的思想时,我绝望地叹了口气。在不知不觉中,我将在令人讨厌的技术中心城镇中为企业客户做 Java 和 .NET 咨询工作,在环路之外,填写我的 TPS 报告并想知道为什么生活似乎如此空虚。我认为我的下一个项目可能会使用 NSIS 安装,尽管它的所有缺点,就像一种荒谬的类汇编语言,它并没有让我理解 Windows Installer 在做什么。 @Tartley - 使用 InnoSetup,这将为您节省类似汇编的语言 :) 确保您也使用 IStool,它有很大帮助。另外——同意对于简单的安装来说这一切都太复杂了,但我认为他们确实需要这种复杂性来安装像 SQL Server 2008 这样的东西......【参考方案2】:我阅读了WiX 文档,下载了示例,但我仍然遇到很多升级问题。尽管可以指定这些卸载,但次要升级不会执行以前产品的卸载。我花了更多的时间进行调查,发现 WiX 3.5 引入了一个新的升级标签。用法如下:
<MajorUpgrade Schedule="afterInstallInitialize"
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."
AllowDowngrades="no" />
但问题的主要原因是文档中说要使用“REINSTALL=ALL REINSTALLMODE=vomus”参数进行小幅升级,但实际上并没有说这些参数是禁止进行重大升级 - 他们只是停止工作。所以你不应该在重大升级时使用它们。
【讨论】:
【参考方案3】:在最新版本(从 3.5.1315.0 测试版开始)中,您可以使用 MajorUpgrade element 而不是自己的。
例如,我们使用此代码进行自动升级。它可以防止降级,提供本地化错误消息,还可以防止升级已经存在的相同版本(即只升级较低版本):
<MajorUpgrade
AllowDowngrades="no" DowngradeErrorMessage="!(loc.NewerVersionInstalled)"
AllowSameVersionUpgrades="no"
/>
【讨论】:
Bob Arnson 的blog post 提供了很多很好的信息。 注意:任何地方都没有记录,但“<MajorUpgrade>
”元素必须放在之后 <Package>
。否则,candle
会给出以下错误:“错误 CNDL0107:架构验证失败,第 1 行第 473 列出现以下错误:命名空间 'schemas.microsoft.com/wix/2006/wi' 中的元素 'Product' 在命名空间'中具有无效的子元素 'MajorUpgrade' schemas.microsoft.com/wix/2006/wi'。预期的可能元素列表:'Package'。"。
+1 这个答案需要获得尽可能多的支持;选择一个获得 5 倍赞成票但使用较旧方法的答案非常诱人。
只是想指出您不需要指定AllowDowngrades
或AllowSameVersionUpgrades
。他们默认为没有。
我之前的评论是错误的——忽略它。我所描述的在安装时没有抱怨,它没有像我想象的那样升级。在“产品 ID”中添加一个星号。在“产品升级代码”中放置一个实际的 GUID - 永远不要更改它。在“Package Id”中添加一个星号。最后,当您增加“产品版本”中的数字时,它会进行实际更新。【参考方案4】:
以下对我有用。
<Product Id="*" Name="XXXInstaller" Language="1033" Version="1.0.0.0"
Manufacturer="XXXX" UpgradeCode="YOUR_GUID_HERE">
<Package InstallerVersion="xxx" Compressed="yes"/>
<Upgrade Id="YOUR_GUID_HERE">
<UpgradeVersion Property="REMOVINGTHEOLDVERSION" Minimum="1.0.0.0"
RemoveFeatures="ALL" />
</Upgrade>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
请确保 Product 中的 UpgradeCode 与 Upgrade 中的 Id 匹配。
【讨论】:
【参考方案5】:这对我有用,即使是 DOWN 主要成绩:
<Wix ...>
<Product ...>
<Property Id="REINSTALLMODE" Value="amus" />
<MajorUpgrade AllowDowngrades="yes" />
【讨论】:
【参考方案6】:我使用这个网站来帮助我了解有关 WiX 升级的基础知识:
http://wix.tramontana.co.hu/tutorial/upgrades-and-modularization
之后我创建了一个示例安装程序(安装了一个测试文件),然后创建了升级安装程序(安装了 2 个示例测试文件)。这将使您对该机制的工作原理有一个基本的了解。
正如 Mike 在 Apress 的书“Windows Installer 权威指南”中所说,它将帮助您理解,但它不是使用 WiX 编写的。
另一个很有帮助的网站是这个:
http://www.wixwiki.com/index.php?title=Main_Page
【讨论】:
页面上的示例无法按预期工作wix.tramontana.co.hu/tutorial/upgrades-and-modularization/…。我和它一起玩。当页面声明将被禁止时,甚至可以降级【参考方案7】:我终于找到了解决方案 - 我将其发布在此处,以供可能遇到相同问题的其他人(你们 5 个人):
将产品 ID 更改为 *在产品下添加以下内容:
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Upgrade Id="YOUR_GUID">
<UpgradeVersion
Minimum="1.0.0.0" Maximum="99.0.0.0"
Property="PREVIOUSVERSIONSINSTALLED"
IncludeMinimum="yes" IncludeMaximum="no" />
</Upgrade>
在InstallExecuteSequence下添加:
<RemoveExistingProducts Before="InstallInitialize" />
从现在开始,每当我安装该产品时,它都会删除以前安装的版本。
注意:将升级 ID 替换为您自己的 GUID
【讨论】:
是的,学习 WiX 就像试图找出某人认为“有意义”来执行简单操作的晦涩咒语。有点像 UNIX。 另外,“将产品 ID 更改为 *”究竟是做什么的?它每次都会生成一个新的产品ID吗?您的产品不再具有固定 ID 是否会产生后果? - 听起来有点矫枉过正。 @Antony, @Dror Helper:我很确定您不应该在这里使用“*”来生成新的 GUID。 (Upgrade Id="") 内部的 GUID 应该是硬编码和固定的,并且它应该与您的 (Product UpgradeCode="") 属性中的 GUID 匹配。 我认为您应该在那里编辑您的示例,使其没有实际的 GUID。我相信人们会复制并粘贴它并逐字使用它。也许使用“YOUR-PRODUCT'S-UPGRADECODE-GUID-HERE”? 您的示例中有错误。微星的ProductVersion
只支持三个版本字段;因此,第四个字段根本不会被比较。请参阅msdn.microsoft.com/en-us/library/aa372379(VS.85).aspx中的 VersionMin 和 VersionMax 下的注释【参考方案8】:
我从教程中错过了一段时间(从http://www.tramontana.co.hu/wix/lesson4.php 窃取)导致“已安装此产品的另一个版本”错误:
*小幅更新 是指对一个或几个文件的小幅改动,而这些改动不需要更改产品版本(major.minor.build)。您也不必更改产品 GUID。请注意,当您创建一个在任何方面都与以前不同的新 .msi 文件时,您始终必须更改包 GUID。安装程序会跟踪您已安装的程序,并在用户想要使用这些 GUID 更改或删除安装时找到它们。对不同的包使用相同的 GUID 会使安装程序感到困惑。
次要升级 表示产品版本已经更改的更改。修改 Product 标签的 Version 属性。产品将保持不变,因此您无需更改产品 GUID,但当然需要获得新的包 GUID。
重大升级 表示重大变化,例如从一个完整版本升级到另一个完整版本。更改所有内容:版本属性、产品和包 GUID。
【讨论】:
Package:Id type:AutogenGuid 描述:产品或合并模块的包代码 GUID。编译产品时,不应设置此属性以允许为每个构建生成包代码。编译合并模块时,必须将此属性设置为模块化 guid。 ---- 所以我们不需要关注包裹ID,对吧? 您的链接已失效【参考方案9】:我正在使用最新版本的 WiX (3.0),但无法使上述工作正常进行。但这确实有效:
<Product Id="*" UpgradeCode="PUT-GUID-HERE" ... >
<Upgrade Id="PUT-GUID-HERE">
<UpgradeVersion OnlyDetect="no" Property="PREVIOUSFOUND"
Minimum="1.0.0.0" IncludeMinimum="yes"
Maximum="99.0.0.0" IncludeMaximum="no" />
</Upgrade>
请注意,PUT-GUID-HERE 应与您在产品的 UpgradeCode 属性中定义的 GUID 相同。
【讨论】:
【参考方案10】:我建议看看 Alex Shevchuk 的教程。他通过 From MSI to WiX, Part 8 - Major Upgrade 的一个很好的动手示例解释了通过 WiX 进行“重大升级”。
【讨论】:
感谢该文章的链接...太棒了!【参考方案11】:以下是我用于重大升级的语法:
<Product Id="*" UpgradeCode="PUT-GUID-HERE" Version="$(var.ProductVersion)">
<Upgrade Id="PUT-GUID-HERE">
<UpgradeVersion OnlyDetect="yes" Minimum="$(var.ProductVersion)" Property="NEWERVERSIONDETECTED" IncludeMinimum="no" />
<UpgradeVersion OnlyDetect="no" Maximum="$(var.ProductVersion)" Property="OLDERVERSIONBEINGUPGRADED" IncludeMaximum="no" />
</Upgrade>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
正如@Brian Gillespie 所指出的,还有其他地方可以根据所需的优化安排 RemoveExistingProducts。请注意 PUT-GUID-HERE 必须相同。
【讨论】:
我正在阅读 Nick Ramirez 关于 Wix 的书中的“升级和修补”部分,他指出,如果您在 InstallInitialize 之后安排 RemoveExistingProducts,那么您还必须安排<InstallExecute After="RemoveExistingProducts" />
。你的例子没有这个 - 这是否意味着这本书是错误的?
我从不明确安排 InstallExecute。
我没有。在 WiX v3.6 中,Burn 将使次要升级易于执行,但如果没有 Burn,则需要用户手动交互(必须提供命令行选项),这使得次要升级基本上无用。 :)
@RobMensching:您如何避免安装旧版本而不是新版本?您的答案对我有用(我可以使用 WiX v3.5.2519.0 编译的唯一“重大升级”示例),但可以安装旧版本(之后,我在“添加/删除程序”)。
好的,我刚刚在this answer 中找到了MajorUpgrade element,这正是我想要的,包括防止降级。【参考方案12】:
Product 元素中的 Upgrade 元素与适当的操作调度相结合,将执行您所追求的卸载。请务必列出您要删除的所有产品的升级代码。
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Upgrade Id="00000000-0000-0000-0000-000000000000">
<UpgradeVersion Minimum="1.0.0.0" Maximum="1.0.5.0" Property="PREVIOUSVERSIONSINSTALLED" IncludeMinimum="yes" IncludeMaximum="no" />
</Upgrade>
请注意,如果您对自己的构建非常小心,您可以防止人们意外地安装您的产品的旧版本而不是新版本。这就是最大字段的用途。在构建安装程序时,我们将 UpgradeVersion Maximum 设置为正在构建的版本,但 IncludeMaximum="no" 以防止出现这种情况。
您可以选择有关 RemoveExistingProducts 的计划。我更喜欢在 InstallFinalize 之后安排它(而不是像其他人推荐的那样在 InstallInitialize 之后):
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFinalize"></RemoveExistingProducts>
</InstallExecuteSequence>
这会在复制新文件和注册表项之前保留以前版本的产品。这让我可以将数据从旧版本迁移到新版本(例如,您已将用户首选项的存储从注册表切换到 XML 文件,但您想保持礼貌并迁移他们的设置)。此迁移在 InstallFinalize 之前的延迟自定义操作中完成。
另一个好处是效率:如果有未更改的文件,Windows Installer 不会在您安排 InstallFinalize 之后再次复制它们。如果在 InstallInitialize 之后调度,则先完全删除以前的版本,然后再安装新版本。这会导致不必要的文件删除和重新复制。
有关其他计划选项,请参阅 MSDN 中的 RemoveExistingProducts 帮助主题。本周,链接为:http://msdn.microsoft.com/en-us/library/aa371197.aspx
【讨论】:
@Brian Gillespie:“...如果有未更改的文件...”是什么意思? Windows Installer 决定何时替换文件、AssemblyVersion、AssemblyFileVersion、文件大小...的标准是什么? @donttellya +1 很难学到这一点。RemoveExistingProducts
计划在 InstallFinalize
之后进行,并且 dll 没有更新,因为 assemblyVersion 未更改,但 AssemblyProduct 等其他字段已更改。我不想受到文件比较例程的摆布 - 我只想要以前的应用程序 GONE以上是关于如何实现 WiX 安装程序升级?的主要内容,如果未能解决你的问题,请参考以下文章
如何防止 wix 自定义引导程序卸载 UI 在升级过程中显示