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' 添加到您的&lt;UpgradeVersion&gt; 标签可能会解决此问题。这是我在没有真正使用这些功能的情况下所做的,除了作为打包和组织产品组件的一种方式。 (这可能隐藏了另一个导致问题的微妙设计问题) @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 安装的应用程序吗?

如何在 Wix Bundle 中引用动态命名的 MSI 文件

Wix Custom BA 正确处理升级

如何找到已安装 MSI 文件的升级代码?

如何使用 WiX 和 MSI 进行静默安装和卸载?

Wix静默安装MSI我们可以停止创建目录吗