升级期间未(仅)复制文件

File not copied (only) during upgrade

概述

对于我的产品的新版本 v1.9.0,我创建了一个新的 MSI 安装程序。该应用程序的上一个版本是v1.7.0.

卸载旧版本然后安装新版本就可以了。

但是当我尝试使用 v1.9.0 安装程序更新旧版本时,正好缺少一个文件 (NLog.dll)。所有其他文件都被复制得很好。

我正在使用 heat.exe 为我的应用程序的所有依赖项创建组件。所以 NLog.dll 的处理方式与所有其他文件完全相同,但它是唯一显示这种奇怪行为的文件。

NLog.dll 从升级到 v1.9.0

的日志片段

这些是该文件的相关日志片段。

第 5174 行:

MSI (s) (E4:1C) [17:12:42:343]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

第 37733 行:

MSI (s) (E4:1C) [17:13:12:252]: Executing op: FileRemove(,FileName=NLog.dll,,ComponentId={53AAD98D-AFDB-4D70-ADCC-5305C3174ED5})
RemoveFiles: File: NLog.dll, Directory: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\
MSI (s) (E4:1C) [17:13:12:260]: Verifying accessibility of file: NLog.dll
MSI (s) (E4:1C) [17:13:12:264]: Note: 1: 2318 2:  
MSI (s) (E4:1C) [17:13:12:267]: Note: 1: 2318 2: 

第 50386 行:

MSI (s) (E4:DC) [17:13:22:442]: Executing op: ComponentRegister(ComponentId={D4B31A07-4F5F-4DAA-8280-9A782110477A},KeyPath=C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll,State=3,,Disk=1,SharedDllRefCount=2,BinaryType=0)
1: {C2F509F4-A1F9-4377-89FE-59B4DB664FB7} 2: {D4B31A07-4F5F-4DAA-8280-9A782110477A} 3: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll 
MSI (s) (E4:DC) [17:13:22:442]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

从升级到 v1.9.0 的一些其他文件的日志片段

为了比较,这里是一些随机的其他文件 (NLog.Extensions.Logging.dll) 的日志片段,它按预期更新。

第 5173 行:

MSI (s) (E4:1C) [17:12:42:342]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

第 37738 行:

MSI (s) (E4:1C) [17:13:12:267]: Executing op: FileRemove(,FileName=NLog.Extensions.Logging.dll,,ComponentId={2CE3E451-CBCA-4D6A-953A-0EEC1F23FE33})
RemoveFiles: File: NLog.Extensions.Logging.dll, Directory: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\
MSI (s) (E4:1C) [17:13:12:269]: Verifying accessibility of file: NLog.Extensions.Logging.dll
MSI (s) (E4:1C) [17:13:12:272]: Note: 1: 2318 2:  
MSI (s) (E4:1C) [17:13:12:276]: Note: 1: 2318 2:  

第 50389 行:

MSI (s) (E4:DC) [17:13:22:442]: Executing op: ComponentRegister(ComponentId={0DFB8E8D-FD31-430F-A84B-C21D7BCB296B},KeyPath=C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll,State=3,,Disk=1,SharedDllRefCount=2,BinaryType=0)
1: {C2F509F4-A1F9-4377-89FE-59B4DB664FB7} 2: {0DFB8E8D-FD31-430F-A84B-C21D7BCB296B} 3: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll 
MSI (s) (E4:DC) [17:13:22:442]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

第 51640 行:

MSI (s) (E4:DC) [17:13:23:148]: Executing op: FileCopy(SourceName=c7dqtb1j.dll|NLog.Extensions.Logging.dll,SourceCabKey=filC5C20BE40C004EEC9809A0196347239A,DestName=NLog.Extensions.Logging.dll,Attributes=512,FileSize=24064,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=1.3.0.804,Language=0,InstallMode=58982400,,,,,,,)
MSI (s) (E4:DC) [17:13:23:149]: File: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll;    To be installed;    Won't patch;    No existing file
MSI (s) (E4:DC) [17:13:23:149]: Source for file 'filC5C20BE40C004EEC9809A0196347239A' is compressed
InstallFiles: File: NLog.Extensions.Logging.dll,  Directory: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\,  Size: 24064

日志片段分析

如您所见,NLog.dll 和其他文件的前三个片段看起来几乎相同。但是在 NLog.dll.

的情况下,文件实际被复制的最后一部分只是丢失了

日志中没有任何内容可以(向我)解释为什么 NLog.dll 没有被复制。

原始 v1.7.0 安装的日志片段

我不知道这是否相关,但这是原始安装中这两个文件的相关日志片段。也许这些很重要,因为它们在原始安装期间具有 ID。

第 5316 行(NLog.dll):

MSI (s) (E4:94) [17:11:14:734]: Executing op: ComponentRegister(ComponentId={53AAD98D-AFDB-4D70-ADCC-5305C3174ED5},KeyPath=C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
1: {D6410853-B366-4D05-A1A3-93FC3EFF982A} 2: {53AAD98D-AFDB-4D70-ADCC-5305C3174ED5} 3: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll 
MSI (s) (E4:94) [17:11:14:734]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

第 5319 行(NLog.Extensions.Logging.dll):

MSI (s) (E4:94) [17:11:14:734]: Executing op: ComponentRegister(ComponentId={2CE3E451-CBCA-4D6A-953A-0EEC1F23FE33},KeyPath=C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
1: {D6410853-B366-4D05-A1A3-93FC3EFF982A} 2: {2CE3E451-CBCA-4D6A-953A-0EEC1F23FE33} 3: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll 
MSI (s) (E4:94) [17:11:14:735]: WIN64DUALFOLDERS: Substitution in 'C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll' folder had been blocked by the 1 mask argument (the folder pair's iSwapAttrib member = 0).

第 19183 行(NLog.dll):

MSI (s) (E4:94) [17:11:17:881]: Executing op: FileCopy(SourceName=NLog.dll,SourceCabKey=fil3052FED1115C64C0B25CEB4ED20F217C,DestName=NLog.dll,Attributes=512,FileSize=422400,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=5.0.0.0,Language=0,InstallMode=58982400,,,,,,,)
MSI (s) (E4:94) [17:11:17:882]: File: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.dll;   To be installed;    Won't patch;    No existing file
MSI (s) (E4:94) [17:11:17:882]: Source for file 'fil3052FED1115C64C0B25CEB4ED20F217C' is compressed
InstallFiles: File: NLog.dll,  Directory: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\,  Size: 422400

第 31615 行(NLog.Extensions.Logging.dll):

MSI (s) (E4:94) [17:11:22:509]: Executing op: FileCopy(SourceName=c7dqtb1j.dll|NLog.Extensions.Logging.dll,SourceCabKey=filC5C20BE40C004EEC9809A0196347239A,DestName=NLog.Extensions.Logging.dll,Attributes=512,FileSize=8704,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=1.0.0.0,Language=0,InstallMode=58982400,,,,,,,)
MSI (s) (E4:94) [17:11:22:509]: File: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\NLog.Extensions.Logging.dll;    To be installed;    Won't patch;    No existing file
MSI (s) (E4:94) [17:11:22:509]: Source for file 'filC5C20BE40C004EEC9809A0196347239A' is compressed
InstallFiles: File: NLog.Extensions.Logging.dll,  Directory: C:\Program Files (x86)\Cisco\DiagnosticBridge\bin\,  Size: 8704

一些相关的配置片段

为了收集依赖关系,我 运行 heat.exe 像这样:

"%WIX%\bin\heat" dir "$(ProjectDir)\obj\PublishOutput" -dr BinFolder -ke -srd -sreg -cg MyComponentGroup -var var.TempPublishDir -gg -out "$(ProjectDir)\obj\MyContent.wxs"

生成的组件如下所示:

<Component Id="cmpE15B2B75697ADA78CA21A063FF464A7F" Directory="BinFolder" Guid="{876C7C40-4FD9-464E-9282-5CE83B56C4C9}">
    <File Id="fil3052FED1115C64C0B25CEB4ED20F217C" KeyPath="yes" Source="$(var.TempPublishDir)\NLog.dll" />
</Component>
<Component Id="cmpF5B8522DF5AB91BD2DBBA73CBCD944B8" Directory="BinFolder" Guid="{EFB396C4-4F10-4C1B-92FB-75D5C616A708}">
    <File Id="filC5C20BE40C004EEC9809A0196347239A" KeyPath="yes" Source="$(var.TempPublishDir)\NLog.Extensions.Logging.dll" />
</Component>

以及产品定义的相关部分:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
    <Product Id="*" Name="xxx" Language="1033" Version="1.9.0.0" Manufacturer="xxx" UpgradeCode="ab9f8a5a-aa60-4327-9299-3f928136a6e4">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    </Product>
</Wix>

我考虑过的事情

所有这些并不能真正解释为什么升级对所有其他文件都能正常工作,但对一个文件却不行。我也用类似的安装程序部署了我产品的几个以前版本,我以前从未遇到过这个问题。

您正在尝试降级 NLog.dll 的版本。 MSI 中存在导致文件被删除而不是重新安装的错误。唯一的解决方法是完全卸载然后重新安装。

解决这个问题的关键提示来自 Bob Arnson:

You're trying to downgrade the version of NLog.dll. There's a bug in MSI that causes the file to be removed and not reinstalled.

事实上,我的软件的最新版本引用的 NLog.dll 版本比我的软件的以前版本要旧。

NLog.dll其实是我软件的间接依赖。我引用的是 NLog.Web.AspNetCore.dll,它又引用了 NLog.Extensions.Logging.dllNLog.dll,所以我都没有意识到有NLog.dll.

的版本降级

强迫客户 uninstall/reinstall 不是一种选择。但是我找到了解决方案 here.

我创建了以下 XSL 转换,它从热输出中删除了三个单独的 NLog 组件,而是插入了一个包含所有三个文件的新组件,并且在我直接引用的文件上设置了 KeyPath:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi" xmlns="http://schemas.microsoft.com/wix/2006/wi" exclude-result-prefixes="xsl wix">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
  <xsl:strip-space elements="*"/>

  <!-- Identity transform -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Remove individual components for NLog files and add the files in a single component, see CT-1886 -->
  <!--  -->
  <xsl:key name="NLogFilesToRemove" match="wix:Component[contains(wix:File/@Source, 'NLog.dll') or contains(wix:File/@Source, 'NLog.Extensions.Logging.dll') or contains(wix:File/@Source, 'NLog.Web.AspNetCore.dll')]" use="@Id" />
  <xsl:template match="*[self::wix:Component or self::wix:ComponentRef][key('NLogFilesToRemove', @Id)]" />
  <xsl:template match="wix:ComponentGroup">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <Component Id="cmpBundledNLog" Directory="BinFolder" Guid="0d590e51-18bd-455a-9edf-c4ff34cce42e">
        <File Id="fileNLogDll" KeyPath="no" Source="$(var.TempPublishDir)\NLog.dll" />
        <File Id="fileNLogExtensionsLoggingDll" KeyPath="no" Source="$(var.TempPublishDir)\NLog.Extensions.Logging.dll" />
        <File Id="fileNLogWebAspNetCoreDll" KeyPath="yes" Source="$(var.TempPublishDir)\NLog.Web.AspNetCore.dll" />
      </Component>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

我不确定这会在未来造成的并发症(在某些时候我想摆脱这种解决方法),但现在它似乎工作正常。

您正在尝试降级 NLog.Extensions.Logging.dll MSI 的默认配置不允许这样做,但是您可以通过显式指定安装程序的 REINSTALLMODE 来覆盖它。

这可以通过覆盖主 wix 文件的产品部分中的相应 属性 以允许降级来实现

<Product Id="*"
       Name="MY SOFTWARE $(var.Version) $(var.Platform)"
       Language="1033"
       Version="$(var.Version)"
       Manufacturer="The Umbrela Corporation"
       UpgradeCode="someguid-guid-guid-guid-someguid">
...   
<Property Id="REINSTALLMODE" Value="dmus"/>
...