从C++CLI工程的依赖库引用问题看.Net加载程序集机制
生活随笔
收集整理的這篇文章主要介紹了
从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.dllhttp://www.code.microsoft.com/de/myAssembly/myAssembly.dllhttp://www.code.microsoft.com/bin/de/myAssembly.dllhttp://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加载程序集机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AntDesignBlazor示例——暗
- 下一篇: 如何使用.NET在2.2秒内处理10亿行