如何引用具有相同名称的2个不同的DLL?

问题描述 投票:3回答:1

我正在开发使用Matrox成像库(MIL)的软件。 这个软件过去使用的是MIL版本9,现在我们转到了v10。由于向后兼容性,我们必须继续支持v9。

使用MIL及其DLL时遇到一些困难:

  1. MIL 9和MIL 10无法同时安装。它也没有任何意义。
  2. MIL 9和MIL 10的C#DLL都命名为Matrox.MatroxImagingLibrary.dll
  3. 两个DLL中的命名空间是相同的。

虽然这对于可交换性非常有用(尽管某些功能已经改变),但并行使用却是一个大问题。 由于文件名和命名空间相同,我无法在同一个程序集中引用这两个DLL,所以我为每个创建了一个程序集, imaging-system_mil9imaging-system_mil10。 这是必要的,但到目前为止还没用。我需要的是一个通用程序集中的基类,以便我可以使用继承,因此我创建了程序集 imaging-system。 在这个程序集中,我为MIL命令添加了自己的包装器。这似乎是一个非常好的解决方案,当我最初在安装了MIL 9的开发计算机上开发和测试时,效果非常好。当我移动到另一台计算机并使用MIL 10进行开发和测试时,我发现我的包装器中的一些命令需要进行调整,因为它们在MIL 10 C#DLL中已经发生了变化。到现在为止还挺好。

今天我回到了我的MIL 9计算机,想要测试更多的东西,但我的测试程序未能启动,说MissingMethodException。经过一番搜索,我发现我完全忘记找到一个解决方案:相同的文件名: 我的测试程序引用了imaging-systemimaging-system_mil9imaging-system_mil10。最后两个都引用了一个文件Matrox.MatroxImagingLibrary.dll,因此bin文件夹中的输出看起来像:

test.exe
imaging-system.dll
imaging-system_mil9.dll
imaging-system_mil10.dll
Matrox.MatroxImagingLibrary.dll (the one from MIL 9)
Matrox.MatroxImagingLibrary.dll (the one from MIL 10)

如您所见,最后两个文件具有相同的名称,因此它基本上就像是一个被另一个文件覆盖的彩票。

我必须解决这个问题的第一个想法是将文件重命名为 Matrox.MatroxImagingLibrary9.dllMatrox.MatroxImagingLibrary10.dll。 这适用于第一个编译级别的时候 imaging-system_mil9.dllimaging-system_mil10.dll 编译是因为它们直接引用相应的文件。其中一个bin文件夹中的输出:

imaging-system_mil10.dll
Matrox.MatroxImagingLibrary10.dll

但是当编译程序集没有直接引用Matrox DLL时,它在下一级失败。编译器只是跳过重命名的文件,很可能是因为程序集名称不再与文件名匹配。这里的bin文件夹:

test.exe
imaging-system.dll
imaging-system_mil9.dll
imaging-system_mil10.dll
missing: Matrox.MatroxImagingLibrary9.dll, Matrox.MatroxImagingLibrary10.dll

此外,手动将重命名的文件复制到EXE的输出文件夹也无济于事,因为EXE不会“看到”它们。这是有道理的:假设有1000个DLL,并且没有一个被命名为程序正在寻找的程序集。怎么找到它?它无法加载所有1000个DLL ...因此文件名必须与程序集名称匹配。

我的下一个想法是为Matrox DLL设置CopyLocal = false,并通过构建后事件将它们分别复制到dll\mil9中。 dll\mil10子文件夹。 每个程序集都将运行预构建或后构建PowerShell脚本,该脚本复制所有引用的DLL的所有dll子文件夹中的所有内容。 每个EXE都将获得app.config中描述的改编的How to save DLLs in a different folder when compiling in Visual Studio?文件。

问题:我之前没有这样做,因为没有必要这样做。因此,我目前面临几个问题: 1)EXE会找到正确的Matrox DLL,因为它在搜索子文件夹时会看到它们吗? DLL具有相同的名称,相同的文化和相同的publicKeyToken,但版本号不同,因此它们可以相互区分。 2)如何在构建期间获取引用的DLL路径列表以提供给我的PowerShell脚本,该脚本查找dll子文件夹并复制文件?我想到的唯一方法是阅读csproj文件。


我到目前为止测试的#1: 我已经使用包含控制台EXE和2个DLL的测试解决方案完成了几项测试,这些测试解决了这种情况。我使用了“CopyLocal = false”和“SpecificVersion = true”,我在<probing>文件中尝试了codebase>app.config,但它只适用于其中一个DLL:

测试文件夹结构:

test.exe
dll\testDLL9.DLL
dll\testDLL10.DLL
mil9-x64\mil.net\Matrox.MatroxImagingLibrary.dll
mil10-x64\mil.net\Matrox.MatroxImagingLibrary.dll

测试EXE:

private static void Main ()
{
  Mil10 ();  // when stepping into this, dll\testDLL10.dll is loaded
  Mil9 ();   // when stepping into this, dll\testDLL9.dll is loaded
}

private static void Mil10 ()  // when arriving here, dll\testDLL10.dll has been loaded
{
  testDLL10.CDLL10.Work ();   // when stepping into this, mil10-x64\Mil.net\Matrox.MatroxImagingLibrary.dll is loaded
}

private static void Mil9 ()  // when arriving here, dll\testDLL9.dll has been loaded
{
  testDLL9.CDLL9.Work ();   // when stepping into this, MissingMethodException is thrown, which is correct, because the EXE uses the already loaded DLL, which is the wrong one.
}

现在,当首先调用Mil9()时,它也会加载 mil10-x64\Mil.net\Matrox.MatroxImagingLibrary.dlltestDLL9.CDLL9.Work()被召唤时,这显然是完全错误的。为什么会这样? 它仅在我删除对testDLL10的引用并注释掉相关函数时才有效。

的app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="dll" />

      <dependentAssembly>
        <assemblyIdentity name="Matrox.MatroxImagingLibrary"
                          publicKeyToken="5a83d419d44a9d98"
                          culture="neutral" />
        <codeBase version="9.2.1109.1" href="mil9-x64\Mil.net\Matrox.MatroxImagingLibrary.dll" />
      </dependentAssembly>

      <dependentAssembly>
        <assemblyIdentity name="Matrox.MatroxImagingLibrary"
                          publicKeyToken="5a83d419d44a9d98"
                          culture="neutral" />
        <codeBase version="10.30.595.0" href="mil10-x64\Mil.net\Matrox.MatroxImagingLibrary.dll" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

最后的说明:

  • DLL不必同时加载,因为这是不可能的,因为MIL 9和MIL 10不能并行安装,见上文。
  • 我已经阅读了Referencing DLL's with same name,但到目前为止我还不想按照答案的第3步建议手动加载DLL。我更喜欢CLR为我加载了正确的DLL。
  • 我读过How to reference different assemblies with the same name?。我无法使用GAC,因为我需要能够通过更改文件夹在不同版本的软件之间切换。与操作系统的连接尽可能少。
c# .net dll
1个回答
2
投票

EXE会找到正确的Matrox DLL [...]吗?

不,如果使用<probing>,因为这只会将某些文件夹添加到搜索引用的程序集时检查的文件夹列表中。如果找到具有所请求名称的文件,则使用此文件。不执行版本检查。如果这是正确的文件,它的工作原理。如果是错误的文件,则会失败。如果已加载具有所请求名称的文件,则无论该版本是否与所请求的版本匹配,都将在以后重复使用。 是的,如果使用<codebase>,因为这包括版本检查。

经过大量测试并在互联网上进一步阅读后,我发现问题的原因是加载了错误的文件: MS Doc "How the Runtime Locates Assemblies"在“步骤1:检查配置文件”一章中说明:

首先,公共语言运行库检查应用程序配置文件,以获取覆盖存储在调用程序集清单中的版本信息的信息。

所以我想“好吧,让我们看看清单,看它是否包含一些有用的数据。”我打开它发现......没什么。所以我检查了输出文件夹中的其他文件,testAPP.exe.config引起了我的注意。直到那时我才认为它是我创建的app.config的简单副本,但令人惊讶的是,除了我的内容之外它还包含另一个非常相关的块,它立即引起了我的注意:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
    <assemblyIdentity name="Matrox.MatroxImagingLibrary" publicKeyToken="5a83d419d44a9d98" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-10.30.595.0" newVersion="10.30.595.0" />
  </dependentAssembly>
</assemblyBinding>

这就是为什么我的测试程序总是试图加载v10库的原因。我的下一个问题是:这到底是怎么回事? 因此我搜索了“c#compiler添加程序集版本重定向”并找到了How to: Enable and Disable Automatic Binding Redirection,其中说:

对于面向.NET Framework 4.5.1及更高版本的Windows桌面应用程序,默认情况下会启用自动绑定重定向。编译应用程序时,绑定重定向将添加到输出配置(app.config)文件中,并覆盖可能发生的程序集统一。源app.config文件未被修改。

在VS 2015及更低版本中,必须手动编辑csproj文件: 将<AutoGenerateBindingRedirects>设为false。 您也可以删除整个条目,但设置为false应确保以后不会自动重新添加true。 在此编辑之后,输出配置文件与我的源文件(包括任何换行符和空行)完全相同。最后,我的测试EXE正好加载了它所需的那些DLL,并且在正确的时间以正确的顺序加载:

'testAPP.vshost.exe' (CLR v4.0.30319: testAPP.vshost.exe): Loaded 'D:\Visual Studio Projects\testTKS_vs2015\testAPP\bin\x64\Debug\dll\testDLL9.dll'. Symbols loaded.
'testAPP.vshost.exe' (CLR v4.0.30319: testAPP.vshost.exe): Loaded 'D:\Visual Studio Projects\testTKS_vs2015\testAPP\bin\x64\Debug\mil9-x64\Mil.net\Matrox.MatroxImagingLibrary.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'testAPP.vshost.exe' (CLR v4.0.30319: testAPP.vshost.exe): Loaded 'D:\Visual Studio Projects\testTKS_vs2015\testAPP\bin\x64\Debug\dll\testDLL10.dll'. Symbols loaded.
'testAPP.vshost.exe' (CLR v4.0.30319: testAPP.vshost.exe): Loaded 'D:\Visual Studio Projects\testTKS_vs2015\testAPP\bin\x64\Debug\mil10-x64\Mil.net\Matrox.MatroxImagingLibrary.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.

YEAH! :-)

剩下的唯一问题是编译器警告:

1>------ Build started: Project: testAPP, Configuration: Debug x64 ------
1>  No way to resolve conflict between "Matrox.MatroxImagingLibrary, Version=10.30.595.0, Culture=neutral, PublicKeyToken=5a83d419d44a9d98" and "Matrox.MatroxImagingLibrary, Version=9.2.1109.1, Culture=neutral, PublicKeyToken=5a83d419d44a9d98". Choosing "Matrox.MatroxImagingLibrary, Version=10.30.595.0, Culture=neutral, PublicKeyToken=5a83d419d44a9d98" arbitrarily.
1>  Consider app.config remapping of assembly "Matrox.MatroxImagingLibrary, Culture=neutral, PublicKeyToken=5a83d419d44a9d98" from Version "9.2.1109.1" [] to Version "10.30.595.0" [] to solve conflict and get rid of warning.
1>C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(1820,5): warning MSB3276: Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.
1>  testAPP -> D:\Visual Studio Projects\testTKS_vs2015\testAPP\bin\x64\Debug\testAPP.exe
========== Build: 1 succeeded, 0 failed, 2 up-to-date, 0 skipped ==========

也许我可以以某种方式禁用它。


How can I get a list of referenced DLL paths during build to feed into my PowerShell script that looks for dll subfolders and copies files?

我很可能不会。

我将编写一个可以完成工作的小型C#程序:它将获取项目文件的名称,读取它并搜索所有项目引用和基于文件的引用。然后,它将在这些引用的项目和文件的文件夹中查找dll子文件夹,并将内容复制到本地dll子文件夹。


最后的说明:

  • 在我的测试中,我使用<codebase>成功引用了我的弱程序集:

该组件没有publicKeyToken,所以我只是把它留下:

  <!-- probing privatePath="dll" /-->
  <dependentAssembly>
    <assemblyIdentity name="testDLL9" culture="neutral" />
    <codeBase version="1.0.0.0" href="dll\testDLL9.dll" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="testDLL10" culture="neutral" />
    <codeBase version="1.0.0.0" href="dll\testDLL10.dll" />
  </dependentAssembly>
© www.soinside.com 2019 - 2024. All rights reserved.