【发布时间】:2026-02-05 16:15:01
【问题描述】:
我正在开发使用 Matrox Imaging Library (MIL) 的软件。
该软件过去使用 MIL 的第 9 版,现在我们迁移到 v10。由于向后兼容,我们必须继续支持 v9。
在使用 MIL 及其 DLL 时存在一些困难:
- MIL 9 和 MIL 10 不能同时安装。这也没有任何意义。
- MIL 9 和 MIL 10 的 C# DLL 都命名为
Matrox.MatroxImagingLibrary.dll。 - 两个 DLL 中的命名空间是相同的。
虽然这对于可交换性非常有用(尽管某些功能已经改变),但对于并行使用来说却是一个大问题。
由于文件名和命名空间相同,我无法在同一个程序集中引用两个 DLL,因此我为每个程序创建了一个程序集,imaging-system_mil9 和imaging-system_mil10。
这是必要的,但到目前为止毫无用处。我需要的是通用程序集中的基类,以便我可以使用继承,因此我创建了程序集imaging-system.
在这个程序集中,我为 MIL 命令添加了自己的包装器。
当我最初在安装了 MIL 9 的开发计算机上进行开发和测试时,这似乎是一个非常好的解决方案并且运行良好。当我转移到另一台计算机并使用 MIL 10 进行开发和测试时,我发现我的包装器中的一些命令需要进行调整,因为它们在 MIL 10 C# DLL 中发生了更改。到现在为止还挺好。
今天我搬回了我的 MIL 9 计算机并想测试更多的东西,但我的测试程序无法启动,说 MissingMethodException。经过一番搜索,我发现我完全忘记了找到一个解决方案:相同的文件名:
我的测试程序引用了imaging-system、imaging-system_mil9 和imaging-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。