【问题标题】:How can I reference 2 different DLLs that have the same name?如何引用 2 个具有相同名称的不同 DLL?
【发布时间】:2026-02-05 16:15:01
【问题描述】:

我正在开发使用 Matrox Imaging Library (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_mil9
imaging-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.dll
Matrox.MatroxImagingLibrary10.dll

imaging-system_mil9.dll
imaging-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,并且没有一个 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 路径列表,以馈送到查找 dll 子文件夹和副本的 PowerShell 脚本中文件?我想到的唯一方法是阅读csproj文件。


到目前为止,我在 #1 上测试的内容:
我已经使用包含控制台 EXE 和 2 个 DLL 的测试解决方案进行了几次测试,这些测试解决方案复制了这种情况。 我使用了“CopyLocal=false”和“SpecificVersion=true”,我在app.config 文件中尝试了<probing>codebase>,但它只适用于其中一个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.dll
testDLL9.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,因为我需要能够通过更改文件夹来在不同版本的软件之间切换。与操作系统的连接尽可能少。

【问题讨论】:

  • 如何创建 2 个相同的包装类,它们都基于相同的接口,其中 v9 有一些抛出 NotImplementedException 的函数,而 v10 已经实现了所有函数。然后在运行时实例化正确的(这将加载正确的底层 dll)。
  • @Neil:这就是我所做的,也许我描述得不够详细。我的问题只是加载正确的 DLL。

标签: c# .net dll


【解决方案1】:

EXE 能否找到正确的 Matrox DLL [...]?

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

在网上大量测试和进一步阅读后,我找到了加载错误文件的问题的原因:
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#编译器添加程序集版本重定向”,发现How to: Enable and Disable Automatic Binding Redirection,上面写着:

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

在 VS 2015 及以下版本中,csproj 文件必须手动编辑:
&lt;AutoGenerateBindingRedirects&gt; 设置为false
您也可以删除整个条目,但设置为 false 应确保以后不会自动重新添加 true
在此编辑之后,输出配置文件与我的源文件 100% 相同(包括任何换行符和空行)。最后,我的测试 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.

是的! :-)

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

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 ==========

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


如何在构建期间获取引用的 DLL 路径列表,以将其输入到查找 dll 子文件夹并复制文件的 PowerShell 脚本中?

很可能我做不到。

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


最后的笔记:

  • 在测试期间,我使用&lt;codebase&gt; 成功引用了我的weak 程序集:

程序集没有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>

【讨论】:

  • 谢谢你!我仍在为这个问题苦苦挣扎(我是您链接问题的作者),并设法用您提供的代码解决了这个问题!
最近更新 更多