从C++CLI工程的依赖库引用问题看.Net加载程序集机制
2024/1/14 14:02:29
本文主要是介绍从C++CLI工程的依赖库引用问题看.Net加载程序集机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
问题
最近在为某第三方MFC项目写C++/CLI工程插件时遇到了如下一个问题:
MFC的工程不允许把.Net的依赖程序集放到执行程序的目录(防止影响其稳定性),依赖库只能放到非执行程序子目录的其他目录中。但无论是调用
// 使用windows API 需要 #include <windows.h> SetDllDirectory(L"PathToDll"); // 使用.Net API的CLI写法 System::Domain::CurrentDomain->AppendPrivatePath(L"PathToDll");
或者加到PATH环境变量中,均无法加载依赖的第三方引用库。
问题处理方案
最终通过使用AssemblyResolve事件来手动加载程序集解决了该问题,参见参考链接1.
解答
这个问题的出现是因为不了解.Net的程序集的搜索与加载机制。C++/CLI的dll加载.Net依赖库时是完全按照.Net机制进行的,而.Net对程序集的加载顺序与搜索,有其自己的机制,在其官方文档中有详细描述(参考链接2)。简单来说按如下顺序。
加载顺序
- 根据App.Config或者发布者策略文件或者机器配置文件(这三个文件语法相同,位置不一样)中的决定依赖的程序集的版本号信息。
- 检查以前是否加载过该程序集,如果有直接加载,如果有失败记录直接失败。
- 针对强签名的程序集检查GAC中是否包含,有就直接加载。
- 根据已经读取的配置信息开始进行程序集的探测。
程序集的探测
- 如果配置文件没有对程序集的相关描述,且程序集的加载请求是通过Assembly.LoadFrom("path")发起的,则会直接根据指定的路径加载。
- 如果配置文件配置了
<codebase>
节点则会直接按指定的相对路径或URL加载。找不到则直接失败。官方文档
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="AssemblyRef"/> <codeBase href="Ref\AssemblyRef.dll"/> </dependentAssembly> </assemblyBinding> </runtime> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> </configuration>
- 如果有
<probing>
节点的话,则会通过子目录查找加载,注意只允许相对目录或URL,这里其实和AppendPrivatePath
或者AppDomainSetup.PrivateBinPath
都是一回事。官方文档
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="Ref1"/> </assemblyBinding> </runtime> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> </configuration>
注意在probling的过程中,有其相应的规则。受基目录,语言,程序集名称,和<probing>
节点配置(注意这里不止App.Config还有另外两个配置文件)相关。
官方举例如下:
- 程序集: myAssembly
- 基目录:
http://www.code.microsoft.com
-
[<probing>
节点配置: bin - 语言: de
那么运行时会从以下四个URL尝试搜索加载: http://www.code.microsoft.com/de/myAssembly.dll
http://www.code.microsoft.com/de/myAssembly/myAssembly.dll
http://www.code.microsoft.com/bin/de/myAssembly.dll
http://www.code.microsoft.com/bin/de/myAssembly/myAssembly.dll
注意事项
- 可以使用probing工具进行检查尝试的probling的路径Fuslogvw.exe (Assembly Binding Log Viewer) - .NET Framework | Microsoft Learn(需要管理员权限启动,下面会给出一个例子)。
- 只有强签名的程序集才有版本号检查。
- 子目录内的应用程序集不会主动搜索加载,需要在包括在
privatePath
的内。 - 不管
<codebase>
还是<probing>
节点的描述都不允许脱离执行应用程序根目录,以下一个Fuslogvw.exe工具提供的日志说明了这一点。配置文件中使用<probing privatePath="..\Ref1"/>
来指定了appbase目录外的Ref1目录作为搜索目录。但拼接出来的路径警告: 不是探测位置。最终加载失败。
*** 程序集联编程序日志项 (1/13/2024 @ 12:06:12 PM) *** 操作失败。 绑定结果: hr = 0x80070002。系统找不到指定的文件。 程序集管理器加载位置: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 在可执行文件下运行 C:\Users\zhang\Desktop\myproject\AssemblyLoadDemo\output\console\DemoConsoleApp.exe --- 详细的错误日志如下。 === 预绑定状态信息 === 日志: DisplayName = AssemblyRef, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified) 日志: Appbase = file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/ 日志: 初始 PrivatePath = NULL 日志: 动态基 = NULL 日志: 缓存基 = NULL 日志: AppName = DemoConsoleApp.exe 调用程序集: DemoConsoleApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null。 === 日志: 此绑定从 default 加载上下文开始。 日志: 在配置文件中找到专用路径提示: ..\Ref1。 日志: 正在使用应用程序配置文件: C:\Users\zhang\Desktop\myproject\AssemblyLoadDemo\output\console\DemoConsoleApp.exe.Config 日志: 使用主机配置文件: 日志: 使用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config 的计算机配置文件。 日志: 此时没有为引用应用策略(私有、自定义、分部或基于位置的程序集绑定)。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef.DLL,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef/AssemblyRef.DLL,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef.EXE,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef/AssemblyRef.EXE,因为该位置在 appbase 范围以外。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef.DLL。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef/AssemblyRef.DLL。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef.EXE。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef/AssemblyRef.EXE。 日志: 已尝试所有探测 URLs 但全部失败。 *** 程序集联编程序日志项 (1/13/2024 @ 12:06:12 PM) *** 操作失败。 绑定结果: hr = 0x80070002。系统找不到指定的文件。 程序集管理器加载位置: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 在可执行文件下运行 C:\Users\zhang\Desktop\myproject\AssemblyLoadDemo\output\console\DemoConsoleApp.exe --- 详细的错误日志如下。 === 预绑定状态信息 === 日志: DisplayName = AssemblyRef, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified) 日志: Appbase = file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/ 日志: 初始 PrivatePath = NULL 日志: 动态基 = NULL 日志: 缓存基 = NULL 日志: AppName = DemoConsoleApp.exe 调用程序集: DemoConsoleApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null。 === 日志: 此绑定从 default 加载上下文开始。 日志: 在配置文件中找到专用路径提示: ..\Ref1。 日志: 正在使用应用程序配置文件: C:\Users\zhang\Desktop\myproject\AssemblyLoadDemo\output\console\DemoConsoleApp.exe.Config 日志: 使用主机配置文件: 日志: 使用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config 的计算机配置文件。 日志: 此时没有为引用应用策略(私有、自定义、分部或基于位置的程序集绑定)。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef.DLL,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef/AssemblyRef.DLL,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef.EXE,因为该位置在 appbase 范围以外。 警告: 不是探测位置 file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/Ref1/AssemblyRef/AssemblyRef.EXE,因为该位置在 appbase 范围以外。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef.DLL。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef/AssemblyRef.DLL。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef.EXE。 日志: 尝试下载新的 URL file:///C:/Users/zhang/Desktop/myproject/AssemblyLoadDemo/output/console/AssemblyRef/AssemblyRef.EXE。 日志: 已尝试所有探测 URLs 但全部失败。
- 如果程序集有多个不同的版本号的版本的话,使用
<codebase>
而不是<probing>
。
参考链接:
- DLL redirection for c++/cli which load C# DLL (help) (microsoft.com)
- How the Runtime Locates Assemblies - .NET Framework | Microsoft Learn
这篇关于从C++CLI工程的依赖库引用问题看.Net加载程序集机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#
- 2024-01-24Advanced .Net Debugging 1:你必须知道的调试工具
- 2024-01-24.NET集成IdGenerator生成分布式全局唯一ID
- 2024-01-23用CI/CD工具Vela部署Elasticsearch + C# 如何使用
- 2024-01-23.NET开源的简单、快速、强大的前后端分离后台权限管理系统
- 2024-01-23C#对象二进制序列化优化:位域技术实现极限压缩