ClickOnce - 文件已存在错误 - 为什么 ClickOnce 要尝试复制 DLL 文件两次?

22
ClickOnce在确定要复制到客户机器上的dll文件时,只查看应用程序清单文件还是也会调查程序集的内部来确定依赖文件?我问这个问题是因为我在尝试使用ClickOnce启动一个已发布为ClickOnce的WPF .NET 4应用程序时出现了下面的错误: “文件C:\Users\CNelson\AppData\Local\Temp\Deployment\PGX6P33A.35N\AJQL8AC8.D60\tx16_rtf.dll已经存在”。 在我包含对两个第三方.NET dlls的引用之后,这个错误开始出现。这两个引用都引用一个非托管的dll文件(tx16_rtf.dll)。我想让tx16_rtf.dll被复制到客户端PC上的bin文件夹中,所以我将它包含在我的项目中,并将Build Action设置为“Content”,Copy to Output Directory设置为“Copy Always”。 然而,由于某种原因,当我尝试启动应用程序时,ClickOnce会尝试复制文件“tx16_rtf.dll”两次,导致错误。 如果我查看部署清单文件,我可以清楚地看到一个且仅一个文件“tx16_rtf.dll”的条目。那么,我的问题是,为什么ClickOnce会尝试复制文件“tx16_rtf.dll”两次,如果它在部署清单文件中只存在一次呢? 以下是引用'tx16_rtf.dll'的部署清单文件的片段:
  <file name="tx16_rtf.dll" size="839680">
    <hash>
      <dsig:Transforms>
        <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
      </dsig:Transforms>
      <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
      <dsig:DigestValue>V6i2QcARl3+1SJHCugoazb9zrOY=</dsig:DigestValue>
    </hash>
  </file>

你对这个问题的解决方案是什么? - Daniel A. White
我的解决方法是不在项目中引用非托管的dll文件。这样,ClickOnce就不会将它们包含在清单中。相反,我所做的是将非托管的dll文件打包成一个zip文件(将构建操作设置为“内容”),当我的应用程序启动时,它会将非托管的dll文件解压到当前执行目录中……然后一切都像魔术般地正常运行。 - ChrisNel52
4个回答

13
在您的Visual Studio解决方案中,文件是如何添加的?请尝试以下步骤。
将dll添加到您的项目中。
如果您在"引用"中引用了该dll,则请设置dll的属性: Build Action = none,Copy to output directory = "do not copy"。然后删除该引用,重新添加该引用,但指向您本地项目文件夹中的dll。在引用上,将"copy local"设置为true。
如果您没有对该dll进行引用,请设置该dll的属性:Build Action = "copy"。Copy to Output Directory = "Copy always"。
如果您有一个引用,您希望它被包含的原因基于引用,而不是dll属性。如果没有引用,则要特别设置dll以被包括。
同时,请检查应用程序文件对话框,并确保该dll未标记为Include(Prerequisite),而是Include或Include(Required)。

1
我使用mage.exe自动创建清单文件。我觉得我对ClickOnce和清单文件有一个总体的良好理解,但是ClickOnce在处理非托管dll时似乎有一些神秘的操作。然而,试图找到关于ClickOnce内部工作的详细文档似乎是不可能的。 - ChrisNel52
RobinDotNet,您的解释让我找到了解决我的重复dll ClickOnce问题的答案,所以我给您点了赞。 - TWood
13
抱歉晚来参加派对,但为了搜索者的利益,我也遇到了Automapper.Net4.dll的类似问题。问题在于我同时引用了'Automapper.Net4.dll'和'Automapper.dll'(后者引用了Net4.dll本身),按照上面的建议,我删除了有问题的引用,清空了bin文件夹并重新构建了项目。Net 4 dll神奇地重新出现了,因此直接引用不再需要。之后ClickOnce没有出现任何问题。 - JsAndDotNet
1
我已经尝试了另一种方法,向@HockeyJ提到通过NuGet引用AutoMapper。我刚刚在项目的发布选项卡上的应用程序文件对话框中排除了AutoMapper.Net4.dll文件。错误消失了,但是Net4 dll仍然被复制(但是未生成清单和cdf-ms文件)。 - alik
1
此外,在配置中查看应用程序文件非常重要。有时,dll 文件会被标记两次。您可以删除第二个条目并重新测试。 - Juan Acosta

12

您没有提到您是否使用了(咳咳)神奇的 MAGE.EXE 生成部署清单。然而,我也遇到过同样的“文件 x 已经存在”错误,并且这是由于托管程序集通过 P/Invoke 调用本地程序集中的函数引起的。

对于 MAGE.EXE 的 -FromDirectory 参数指定位置中的每个托管程序集,MAGE 将创建一组元素 <dependency><dependentAssembly>...</dependentAssembly></dependency>(包括程序集代码库、标识、大小、哈希值等)。对于每个其他文件(包括非托管本机程序集),MAGE.EXE 将创建一个 <file>...</file> 元素。

但是在安装时,看来 ClickOnce 实际上会检查每个托管程序集的清单元数据。因此,如果您的应用程序有 ManagedAssemblyA,它通过 P/Invoke 调用 NativeAssemblyB 中的代码(或者在您的情况下是 tx16_rtf.dll),则您将通过 ILDASM 看到 ManagedAssemblyA 的清单具有 .module extern NativeAssemblyB.dll 语句。

我只能假设 ClickOnce 在处理 <dependentAssembly codebase="ManagedAssemblyA.dll"> 元素时会检查程序集的元数据,看到引用了本机程序集,又看到它也在相同的部署位置并将其复制下来。然后,在稍后处理 <file name="NativeAssemblyB.dll"> 元素时,它会因为已经复制此文件而导致错误,并认为安装失败是最安全的操作。我没有在 Microsoft 的任何文档中找到这种行为。

因此,解决方案是,在使用 MAGE.EXE 生成部署清单之后但在签名之前,删除任何本机程序集的 <file> 元素。本地程序集仍然需要在与 ClickOnce 应用程序所需的其他程序集相同的部署位置可用。

在我们的情况下,我们已经自动化了这个过程,因为我们还使用每个持续集成构建自动化生成部署清单(而不是使用Visual Studio 2010内部的发布向导,后者会给您更多控制权);我们有一个PowerShell脚本来调用MAGE.EXE创建部署清单,一些更多的PowerShell来操作XML并删除<file>元素(使用PowerShell真的很容易...使用批处理文件祝你好运!),然后我们调用MAGE.EXE对清单进行签名。

很棒的答案!我会研究实施你的解决方案。 - ChrisNel52
你好!自从这个答案发布以来已经很长时间了,所以我想试着联系一下。你上面描述的似乎是我遇到的问题,不知道是否有人能够详细说明“检查程序集元数据,看看是否存在本地程序集引用”的问题 - 它看到了什么?我有没有办法查看这些信息? - Dave

0

如果你有2个引用依赖于同一个第三方nuget包的不同版本,有时候你会有两个对同一个nuget包的引用。这会导致Clickonce失败,所以需要合并你的nuget包。


0
这也可能发生在您有时引用 .csproj 文件,有时引用编译后的 .dll 文件的情况下。

E.g.:

Main.csproj:
  ref A.csproj
  ref B.csproj
A.csproj
  ref B.dll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接