C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计925个文字,预计阅读时间需要4分钟。
动态加载DLL在C++中的应用通常涉及以下步骤:
为什么 Assembly.LoadFrom 加载后根本 unload 不掉
Assembly.LoadFrom 把程序集扔进默认加载上下文(Default Load Context),而这个上下文在 .NET Framework 和 .NET Core 中都**不可卸载**。哪怕你把返回的 Assembly 变量设为 null、手动触发 GC.Collect(),它依然牢牢钉在内存里。
- 现象:多次调用
Assembly.LoadFrom("plugin.dll"),返回的是同一个实例(缓存行为),但你无法清理它 - 后果:插件热更新失败、内存持续增长、类型冲突(比如两个不同版本的
Newtonsoft.Json同时被拉进来) - 调试线索:用
fuslogvw.exe查 Fusion Log,看实际绑定到了哪个路径和版本
AssemblyLoadContext 是唯一可卸载的正解(.NET 5+)
只有显式创建 isCollectible: true 的 AssemblyLoadContext 实例,并全程在其内部完成加载、反射、调用,才能安全调用 Unload()。
- 必须用
context.LoadFromAssemblyPath(path),不能混用静态Assembly.Load*方法 - 类型对象(
Type、object实例)不能跨上下文直接传递,否则抛SerializationException或类型不匹配 - 安全跨上下文调用方式:用
context.ExecuteInLoadContext(() => { /* 创建实例并调用 */ }),或提前封装成Func<object>委托返回 - 卸载前必须确保:无任何对该上下文中类型的引用、无事件订阅、无后台线程持有对象、无静态字段缓存
BindingFlags 不写全,GetMethod 就是 null
反射找方法时,Type.GetMethod("DoWork") 默认只查 public 实例方法。如果目标是 private static void Init(),不加 BindingFlags 就永远返回 null,后续 Invoke 必然炸出 NullReferenceException。
- 查私有静态方法:
GetMethod("Init", BindingFlags.NonPublic | BindingFlags.Static) - 查泛型方法(如
Map<T>()):GetMethods().FirstOrDefault(m => m.Name == "Map" && m.IsGenericMethod),GetMethod("Map")会失败 - 大小写敏感:方法名拼错一个字母,或大小写不符,就找不到——.NET 默认严格区分
依赖项不会自动加载,错误信息里那句 “or one of its dependencies” 才是重点
Assembly.LoadFrom 和 AssemblyLoadContext.LoadFromAssemblyPath 都**不递归解析依赖**。报错 Could not load file or assembly 'XXX' or one of its dependencies 时,“or one of its dependencies” 往往才是真实缺失项。
- 最稳妥做法:把目标 DLL 和它所有直接/间接依赖(包括
System.Data.SqlClient、Utils.dll等)全部放在同一目录下 - 自定义依赖解析:继承
AssemblyLoadContext,重写Load(AssemblyName)方法,在里面按需返回已加载的依赖 - 绝对避免
Assembly.LoadFile:它完全隔离上下文,导致类型无法转换、委托无法序列化,连typeof(T).Assembly == asm都为false
真正的难点不在“怎么加载”,而在“怎么干净地收场”——卸载失败往往不是代码没写 Unload(),而是某个闭包、某条事件链、某个静态字典还在悄悄持有那个上下文里的类型实例。
本文共计925个文字,预计阅读时间需要4分钟。
动态加载DLL在C++中的应用通常涉及以下步骤:
为什么 Assembly.LoadFrom 加载后根本 unload 不掉
Assembly.LoadFrom 把程序集扔进默认加载上下文(Default Load Context),而这个上下文在 .NET Framework 和 .NET Core 中都**不可卸载**。哪怕你把返回的 Assembly 变量设为 null、手动触发 GC.Collect(),它依然牢牢钉在内存里。
- 现象:多次调用
Assembly.LoadFrom("plugin.dll"),返回的是同一个实例(缓存行为),但你无法清理它 - 后果:插件热更新失败、内存持续增长、类型冲突(比如两个不同版本的
Newtonsoft.Json同时被拉进来) - 调试线索:用
fuslogvw.exe查 Fusion Log,看实际绑定到了哪个路径和版本
AssemblyLoadContext 是唯一可卸载的正解(.NET 5+)
只有显式创建 isCollectible: true 的 AssemblyLoadContext 实例,并全程在其内部完成加载、反射、调用,才能安全调用 Unload()。
- 必须用
context.LoadFromAssemblyPath(path),不能混用静态Assembly.Load*方法 - 类型对象(
Type、object实例)不能跨上下文直接传递,否则抛SerializationException或类型不匹配 - 安全跨上下文调用方式:用
context.ExecuteInLoadContext(() => { /* 创建实例并调用 */ }),或提前封装成Func<object>委托返回 - 卸载前必须确保:无任何对该上下文中类型的引用、无事件订阅、无后台线程持有对象、无静态字段缓存
BindingFlags 不写全,GetMethod 就是 null
反射找方法时,Type.GetMethod("DoWork") 默认只查 public 实例方法。如果目标是 private static void Init(),不加 BindingFlags 就永远返回 null,后续 Invoke 必然炸出 NullReferenceException。
- 查私有静态方法:
GetMethod("Init", BindingFlags.NonPublic | BindingFlags.Static) - 查泛型方法(如
Map<T>()):GetMethods().FirstOrDefault(m => m.Name == "Map" && m.IsGenericMethod),GetMethod("Map")会失败 - 大小写敏感:方法名拼错一个字母,或大小写不符,就找不到——.NET 默认严格区分
依赖项不会自动加载,错误信息里那句 “or one of its dependencies” 才是重点
Assembly.LoadFrom 和 AssemblyLoadContext.LoadFromAssemblyPath 都**不递归解析依赖**。报错 Could not load file or assembly 'XXX' or one of its dependencies 时,“or one of its dependencies” 往往才是真实缺失项。
- 最稳妥做法:把目标 DLL 和它所有直接/间接依赖(包括
System.Data.SqlClient、Utils.dll等)全部放在同一目录下 - 自定义依赖解析:继承
AssemblyLoadContext,重写Load(AssemblyName)方法,在里面按需返回已加载的依赖 - 绝对避免
Assembly.LoadFile:它完全隔离上下文,导致类型无法转换、委托无法序列化,连typeof(T).Assembly == asm都为false
真正的难点不在“怎么加载”,而在“怎么干净地收场”——卸载失败往往不是代码没写 Unload(),而是某个闭包、某条事件链、某个静态字典还在悄悄持有那个上下文里的类型实例。

