使用同一个 DLL 的多个版本
Posted
技术标签:
【中文标题】使用同一个 DLL 的多个版本【英文标题】:Using multiple versions of the same DLL 【发布时间】:2011-05-06 20:47:57 【问题描述】:我的任务是为应用程序创建一个新模块,因此,我正在向项目中添加新的 DLL。这一切都很好。
但是,在我的 DLL 中,我想使用新版本的外部 DLL(我无法控制)。如果我只是引用新的 DLL 并只使用那个,我的代码将工作,但旧代码将停止运行。
Could not load file or assembly 'itextsharp, Version=5.0.6.0, Culture=neutral,
PublicKeyToken=8354ae6d2174ddca' or one of its dependencies. The located assembly's
manifest definition does not match the assembly reference. (Exception from HRESULT:
0x80131040)
我尝试了一个更改 DLL 名称的简单技巧,但这显然对我来说有点太天真了,认为它会起作用。我试过使用外部别名(通过在我的引用中定义它们),但我仍然不知道如何将两个同名文件放入一个 BIN 文件夹中......
我该怎么办?
【问题讨论】:
【参考方案1】:假设您的项目结构如下:
...其中A
和B
是类库,C
是可执行类型项目(例如单元测试或控制台项目)。
假设文件夹结构是这样的:
ABC.sln
A/A.csproj
A/...
B/B.csproj
B/...
C/C.csproj
C/...
lib/thirdparty4/thirdparty.dll
lib/thirdparty5/thirdparty.dll
如果我们试图天真地引用我们的项目,我们就会遇到问题:thirdparty.dll
的两个版本将被复制到同一个文件夹中(C
的输出(即 bin)目录)。我们需要一种方法让C
将这两个 dll 复制到其输出目录中,并提供一种机制来引用其中一个。
为了解决这个问题,我修改了C.csproj
以包含以下内容:
<ItemGroup>
<Content Include="..\lib\thirdparty4\thirdparty.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>thirdparty4\thirdparty.dll</Link>
</Content>
<Content Include="..\lib\thirdparty5\thirdparty.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>thirdparty5\thirdparty.dll</Link>
</Content>
</ItemGroup>
这将指示它在其输出目录中创建thirdparty4\thirdparty.dll
和thirdparty5\thirdparty.dll
。
现在,在构建 C
之后,它的输出目录如下所示:
C\bin\Debug\A.dll
C\bin\Debug\B.dll
C\bin\Debug\C.dll
C\bin\Debug\thirdparty4\thirdparty.dll
C\bin\Debug\thirdparty5\thirdparty.dll
为了指示 C
使用这两个 dll,我向其中添加了一个 App.config
文件,其中包含以下内容:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="thirdparty" culture="neutral" publicKeyToken="1234567890123445"/>
<bindingRedirect oldVersion="4.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<codeBase version="4.0.0.0" href="thirdparty4\thirdparty.dll" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="thirdparty" culture="neutral" publicKeyToken="1234567890123445"/>
<bindingRedirect oldVersion="5.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
<codeBase version="5.0.0.0" href="thirdparty5\thirdparty.dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
这将指示程序集根据需要的版本使用一个 DLL 或另一个,这两个都将在输出目录的子文件夹中可用。 (bindingRedirect 元素是可选的,但如果您需要对其进行一系列修订,则可以使用它们。)
【讨论】:
根据您的建议,可以在运行时选择加载哪个程序集?我有 3 个不同的 DLL,它们的方法大多相同等等,但内部工作方式不同,所以我希望能够选择一个版本并在整个过程中使用它。 @coloboxp 如果程序集在编译时已知并且都可以引用,那么可以使用extern alias
和外观。
很好的信息:如果程序集被强命名(例如,Newtonsoft.Json.dll
和 Newtonsoft.Json.12.dll
),也可以将程序集放在具有不同名称的相同输出目录中。
嗨 Jay,我的第三方 dll 是 Microsoft.Azure.DocumentDB.Core.dll。我有相同的项目结构:项目 A 引用 dll 的 v2.1.3(具有批量导入功能)和项目 B 引用 v2.5.1(没有批量导入上限。但有一些其他功能)。在项目 A 中调用方法时,我无法让你的东西正常工作并得到“找不到方法”。你知道吗?谢谢。
对于最终来到这里的任何人仅供参考:在旧 ASP.NET Web 窗体项目的 Web.Config 中执行此操作,我必须将 你可以load another version into a specific AppDomain
可能太详细了,但这里有一篇文章演示了在有用的环境中使用 AppDomain 以及它们是如何工作的:
http://msdn.microsoft.com/en-us/magazine/cc164072.aspx
在一个非常基本的意义上,它归结为这个示例代码:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
...
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
if (/*some condition*/)
return Assembly.LoadFrom("DifferentDllFolder\\differentVersion.dll");
else
return Assembly.LoadFrom("");
【讨论】:
没有我希望的那么漂亮,但它完成了工作。谢谢! 我允许自己编辑您的答案,以包含我用来解决问题的代码示例。但主要原因是第二个环节早就死了。您能否偶然将类似的资源链接到旧资源? @Shaamaan 我无法找到原始链接所指的内容:(这是一个很好的入口页面,可以很好地替代:msdn.microsoft.com/en-us/library/ms172158(v=vs.110).aspx @Shaamaan 。我假设您的 DLL 依赖于其他 dll(具有不同版本)。所以你添加了上面的代码来解决这个问题。是对的吗 ?在您的 DLL 源代码中,您在何处/何时执行上述代码以挂钩到 ResolveEventHandler()。这应该发生在 DLL 初始化中对吧?您是否为此使用了 PreApplicationStartMethod() 的方法?我无法让 PreApplicationStartMethod() 方法工作。 @Feru 请记住这个问题是 5 年前提出的,所以我对确切用例的记忆此时有点模糊。通常,我正在开发的应用程序具有“模块”(想想带有大按钮的主屏幕)。我们的任务是创建一个新模块,因此我们决定将其保留为一个单独的库。然后我们的库使用了一个从未使用过的共享库版本(我认为它与 PDF 相关)。我认为解析代码在主应用程序中,但由于模块化性质,我认为它可以连接到按钮按下。不要引用我的话——再说一遍,那是很久以前的事了。【参考方案3】:如果 AppDomains 解决方案不适用于您的情况,您将面临时间压力,有相互冲突的要求(例如 曾经发生过),并且不介意可笑的人为黑客攻击:
使用ildasm 工具(Visual Studio 中包含的开发人员命令提示符的一部分)反编译较新版本的程序集 编辑生成的 .il 文件以查找/替换程序集命名空间引用。使用引用的示例,这将是从 itextsharp.X 到 itextsharp.new.X 的更改 同样,编辑 AssemblyTitleAttribute 的值。这需要将 ASCII 字符转换为十六进制。 使用ilasm重新编译.il文件 请注意,可能需要对任何依赖程序集重复此操作(例如 - someassembly.core.whatever) 使用不同的名称将新的 .dll 添加到您的项目中并明确引用它们(而不是通过 nuget 或其他方式)嘿,别这样看我。我确实说过可笑做作的黑客......
【讨论】:
【参考方案4】:另一个可行的选择是使用extern
,如下所述:
http://blogs.msdn.com/b/abhinaba/archive/2005/11/30/498278.aspx
【讨论】:
这不能解决运行时问题。 链接已损坏。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…【参考方案5】:您还可以为强命名程序集依赖程序集绑定重定向,如http://msdn.microsoft.com/en-us/library/2fc472t2.aspx 中所述。
您将只有一个版本的文件(最新的),两个引用都会解析到它。
【讨论】:
以上是关于使用同一个 DLL 的多个版本的主要内容,如果未能解决你的问题,请参考以下文章
Jenkins 和 MSBuild 不会构建包含相同 dll 的多个版本的 WiX .msi
如果在同一个 AppDomain 中加载了多个版本,WPF 无法选择正确的 DLL