如何通过CLRProfiler从物理路径加载托管C# DLL而非GAC?
背景:当托管DLL位于GAC中时,我可以通过公钥令牌和程序集名加载,代码如下:
MetaDataAssemblyEmit->DefineAssemblyRef(pbPublicKeyOrToken, publicKeySize, assemblyName, &assemblyMetaData, NULL, 0, 0, &mdAssemblyRef);
现在我希望从物理路径而非GAC加载该DLL引用,尝试了LoadLibrary(L"C:\\Packages\\MyDotNetAssembly.dll");——这段代码已将DLL加载到进程中,但并未加载至CLR运行时。请问能否通过CLRProfiler实现此需求?
当然可以用CLRProfiler实现这个需求!首先得理清核心问题:LoadLibrary是Win32层面的API,它只会把DLL加载到进程的非托管内存空间,但CLR完全不知道这个DLL的存在,自然不会把它当作托管程序集来处理。而CLRProfiler基于CLR的Profiling API,能深度介入CLR的程序集加载流程,正好可以解决这个问题。
具体的实现思路和步骤如下:
第一步:配置并启用自定义CLRProfiler
你需要编写一个自定义的CLRProfiler DLL,实现ICorProfilerCallback3(或更高版本的接口)。然后通过环境变量(比如COR_PROFILER、COR_ENABLE_PROFILING)让目标进程加载你的Profiler,或者通过进程注入的方式完成挂载。第二步:监控程序集加载事件
在Profiler的Initialize回调中,启用COR_PRF_MONITOR_ASSEMBLY_LOADS监控标志,这样CLR会在程序集加载的关键阶段通知你的Profiler。重点关注AssemblyLoadStarted回调——当CLR尝试加载你目标程序集(就是之前用DefineAssemblyRef指定的assemblyName)时,这个回调会触发。第三步:重定向程序集加载路径
当AssemblyLoadStarted触发时,检查当前要加载的程序集名称、版本、公钥令牌是否和你物理路径中的DLL完全匹配。确认匹配后,调用ICorProfilerInfo3::RedirectAssembly方法,把CLR的加载请求重定向到你指定的物理路径(C:\\Packages\\MyDotNetAssembly.dll)。这个方法会告诉CLR放弃原本的GAC查找逻辑,转而从你指定的路径加载程序集。额外注意事项
- 确保物理路径中的DLL和GAC中的目标程序集具有完全一致的名称、版本、公钥令牌——CLR会严格验证程序集的强名称,不匹配的话加载会失败(测试环境可临时关闭强名称验证,但生产环境不建议这么做)。
- 无需再调用
LoadLibrary,CLR在加载托管程序集时会自动把DLL加载到进程中,重复加载可能引发冲突。 - 你的Profiler需要足够的权限,因为要挂钩CLR的内部运行时事件,某些场景下可能需要以管理员身份运行目标进程。
这样操作后,CLR就会从你指定的物理路径加载目标托管DLL,而不是去GAC查找,完全满足你的需求。
内容的提问来源于stack exchange,提问作者anbu2388

