.NET托管程序集引用原生程序集与GAC加载解析机制疑问
问题1:Windows解析原生程序集的具体逻辑是什么?
Windows原生程序集(DLL)的搜索加载逻辑由Windows内核加载器负责,和.NET托管程序集的解析逻辑完全独立,默认规则(Windows XP SP2及以上版本默认开启安全DLL搜索模式,且未手动修改搜索路径、未通过LoadLibrary指定绝对路径、无自定义manifest绑定规则)如下:
- 首先检查目标DLL是否属于系统
KnownDLLs注册表项(路径HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)中的列表,若是则直接加载系统目录下对应版本,跳过后续搜索 - 若不在KnownDLLs列表,按以下顺序检索:
- 应用程序(test.exe)所在的根目录
- 系统目录(调用
GetSystemDirectory()返回的路径,默认为C:\Windows\System32) - 16位系统目录(一般无实际作用,可忽略)
- Windows目录(调用
GetWindowsDirectory()返回的路径,默认为C:\Windows) - 进程当前的工作目录
- 系统环境变量
PATH中配置的所有目录
如果存在以下情况会修改上述默认逻辑:调用SetDllDirectory/AddDllDirectory自定义搜索路径、调用LoadLibrary时传入DLL的绝对路径、DLL所属程序集在应用manifest中指定了绑定的版本和路径。
问题2:当托管程序集因策略重定向从GAC加载时,与其配对的原生dll没有从Windows子目录加载是否属于预期行为?
属于预期行为。
发布者策略重定向、GAC加载都属于.NET CLR的托管程序集解析逻辑,不会影响Windows原生加载器的搜索规则。你场景里的8.0版本System.Fabric.dll从GAC加载后,其内部调用LoadLibrary加载FabricClient.dll时,原生加载器依然会按默认规则检索,你应用本地目录存在同名的7.0版本FabricClient.dll,就会被优先加载,和调用方托管DLL的来源无关。
你遇到的InvalidCastException就是版本不兼容导致的:8.0的托管封装层调用7.0的原生COM接口,二者接口定义不匹配。
问题3:Windows查找原生dll时,是否会优先检索应用程序test.exe所在的同目录?
是,只要目标DLL不在KnownDLLs列表中,也没有自定义的manifest绑定、自定义搜索路径配置,应用程序根目录就是原生DLL搜索的第一优先级,早于系统目录、Windows目录和PATH环境变量路径。
场景修复建议
可任选以下一种方案解决冲突:
- 发布test.exe时不要附带
System.Fabric.dll和FabricClient.dll,直接依赖目标主机上安装的全局Service Fabric Runtime - 在test.exe的应用配置文件(
app.config/test.exe.config)中添加配置,禁用针对System.Fabric程序集的发布者策略重定向,让程序加载本地附带的7.0版本托管程序集,和同目录的原生DLL匹配 - 将test.exe编译时引用的
System.Fabric版本升级到和目标主机Runtime一致的8.0版本,发布时配套附带8.0版本的FabricClient.dll
内容的提问来源于stack exchange,提问作者luka032

