WiX创建了msi文件,奇怪的主要升级行为
Posted
技术标签:
【中文标题】WiX创建了msi文件,奇怪的主要升级行为【英文标题】:WiX created msi file, strange major upgrade behaviour 【发布时间】:2017-11-15 06:13:20 【问题描述】:我正在使用 WiX 工具集从带有 cmake 和 cpack 的 C++ 代码库创建我的 msi 文件。此设置在过去 6 个月中运行良好,但现在我出现了零星的不良行为。
我的安装程序使用相同的升级代码卸载旧产品,以保证我的产品只安装一个版本(总是主要升级)。我在两天前(通过 SCCM)向我的用户推出了最新版本,在 10% 的安装中我遇到了以下问题。
旧安装的卸载有时似乎是错误的。卸载过程删除了文件,但保留了根目录和 bin 目录,但所有文件都被删除了。当我通过 WMI 查询旧产品的安装时,它说它已安装,这很奇怪。其他 90% 的安装程序一切正常。我也无法在本地重现。
我有一个日志文件,但我不确定它是否来自异常安装。
日志文件很长,所以我只发布了sn-ps,这让我很怀疑。
MSI (s) (0C:9C) [15:21:35:083]: Component: CM_CP_runtime.bin.main2.exe;
Installed: Absent; Request: Null; Action: Null
...
MSI (s) (0C:78) [15:21:43:591]: Executing op: FileCopy(SourceName=-dbqfin2.dll|FreeImage.dll,SourceCabKey=CM_FP_runtime.bin.FreeImage.dll,DestName=FreeImage.dll,Attributes=512,FileSize=6201856,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=3.17.0.0,Language=1033,InstallMode=58982400,,,,,,,)
MSI (s) (0C:78) [15:21:43:591]: File: C:\Program Files\LDS Studio\bin\FreeImage.dll; To be installed; Won't patch; No existing file
Action: Null 表示没有操作(但应该是本地的),但我不知道为什么。当我第二次开始安装时,一切正常(因为没有旧版本)。一年前(但仅针对两个单个文件),当我们从 Visual Studio 移植到 cmake 并忘记向我们的 dll 添加一个版本时(以前的 dll 有一个版本,新的没有版本,所以 msi 认为那是不是新版本并在 REINSTALLMODE 默认模式“omus”中跳过它)。从那以后,我们什么都没做,直到 2 天前一切正常。
这是我的 wix_main.wxs 文件
<Product Id="$(var.CPACK_WIX_PRODUCT_GUID)"
Name="$(var.CPACK_PACKAGE_NAME)"
Language="1033"
Version="$(var.CPACK_PACKAGE_VERSION)"
Manufacturer="$(var.CPACK_PACKAGE_VENDOR)"
UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)">
<Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine"/>
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/>
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Upgrade Id="$(var.CPACK_WIX_UPGRADE_GUID)">
<UpgradeVersion
Minimum="1.0.0.0" Maximum="99.0.0.0"
Property="PREVIOUSVERSIONSINSTALLED"
IncludeMinimum="yes" IncludeMaximum="no"
/>
</Upgrade>
<WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/>
<?ifdef CPACK_WIX_PRODUCT_ICON?>
<Property Id="ARPPRODUCTICON">ProductIcon.ico</Property>
<Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/>
<?endif?>
<?ifdef CPACK_WIX_UI_BANNER?>
<WixVariable Id="WixUIBannerBmp" Value="$(var.CPACK_WIX_UI_BANNER)"/>
<?endif?>
<?ifdef CPACK_WIX_UI_DIALOG?>
<WixVariable Id="WixUIDialogBmp" Value="$(var.CPACK_WIX_UI_DIALOG)"/>
<?endif?>
<FeatureRef Id="ProductFeature"/>
<UIRef Id="$(var.CPACK_WIX_UI_REF)" />
<?include "properties.wxi"?>
<?include "product_fragment.wxi"?>
<CustomAction Id="RegisterExtensions"
FileKey="CM_FP_runtime.bin.main.exe"
ExeCommand="-regext"
Execute="deferred"
Return="check"
HideTarget="no"
Impersonate="no"
/>
<CustomAction Id="UnregisterExtensions"
FileKey="CM_FP_runtime.bin.main.exe"
ExeCommand="-unregext"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no"
/>
<InstallExecuteSequence>
<RemoveExistingProducts Before='InstallInitialize'>
NOT REMOVE
</RemoveExistingProducts>
<Custom Action="RegisterExtensions" After="InstallFiles">
NOT REMOVE
</Custom>
<Custom Action="UnregisterExtensions" After="InstallInitialize">
Installed AND (REMOVE = "ALL")
</Custom>
</InstallExecuteSequence>
</Product>
我发现了一些博客文章,人们在其中处理 OnlyDetect 模式,但我不确定它是否对我有帮助,因为我无法在我的机器上重现它。
切换到 REINSTALLMODE amus (https://msdn.microsoft.com/en-us/library/windows/desktop/aa371182(v=vs.85).aspx) 可能是我最后的手段,但那将是我的最后手段。
我找到了这篇博文https://jpassing.com/2007/06/16/where-to-place-removeexistingproducts-in-a-major-msi-upgrade/ => 是不是我必须改变
<RemoveExistingProducts Before='InstallInitialize'>
NOT REMOVE
</RemoveExistingProducts>
到
<RemoveExistingProducts After='InstallInitialize'>
NOT REMOVE
</RemoveExistingProducts>
???
也许有人可以帮我解决这个问题
问候 零陵香豆
【问题讨论】:
升级后旧产品还在,说明升级失败,回滚并尝试重新安装。这与 REINSTALLMODE、RemoveExistingProducts(不需要条件)或 Action=Null 无关。您没有说是否安装了新产品以及旧产品(如果 REP 在事务之外,则可能发生这种情况)。您没有回滚自定义操作,这使事情变得更加复杂。您确实需要完整的失败日志,并且 REP、InstallInitialize、UnRegisterExtensions 的确切顺序相对于彼此和 InstallValidate 并不清楚。 当卸载失败或发生一些奇怪的事情导致它结束时,我有一些奇怪的行为。这导致升级发生,并将旧安装的功能状态迁移到升级中,并且由于某种原因,核心和必需的功能被标记为未安装,因此安装的产品未安装这些功能及其组件。将MigrateFeatures='no'
添加到您的<UpgradeVersion>
标签可能会解决此问题。这是我在没有真正使用这些功能的情况下所做的,除了作为打包和组织产品组件的一种方式。
(这可能隐藏了另一个导致问题的微妙设计问题)
@PhilDW 你的意思是卸载成功,新产品的安装失败,然后它尝试重新安装旧的msi?据我了解,InstallInitialize 之前的 RemoveExistingProducts 不会在升级失败时尝试重新安装旧产品,只有在 InstallInitialize 之后。新产品未安装,WMI 查询说安装了旧版本,但我的目标目录中只有 2 个空文件夹。我没有自定义回滚操作是什么意思,我的两个自定义操作还不够吗?
奇怪的是,msi 安装似乎返回 0 给 SCCM,因为 SCCM 说 msi 安装成功
【参考方案1】:
您看到的最可能的解释是 RemoveExistingProducts(旧产品的卸载)失败。这将导致恢复旧产品的回滚。然后安装旧产品,然后新产品继续安装。在某个时间点,新升级的安装失败并回滚,这可能会损坏旧安装的产品。这是在升级事务之外进行升级 RemoveExistingProducts 的一般问题。如果您的 REP 在 InstallInitize 之后,它将失败并且升级不会继续。
(顺便说一句,如果您在 InstallFinalize 之后和升级事务之外对 REP 进行排序,可能会发生类似的问题,因为您安装了升级产品,事务完成,尝试卸载旧产品但失败,它会回滚并恢复旧产品,因此现在您已安装旧产品和新产品。)
您需要为升级创建详细的 MSI 日志,以查看 REP 失败并安装旧产品的原因,以及新升级失败的原因(否则它会显示在 Program&Features 中)。此外,将 RemoveExistingProducts 保留在升级的事务中更安全。
【讨论】:
以上是关于WiX创建了msi文件,奇怪的主要升级行为的主要内容,如果未能解决你的问题,请参考以下文章
我可以使用 WiX 创建一个 MSI 来升级使用 Burn Bootstrapper 安装的应用程序吗?