appdomain是.net framework中clr提供的进程内隔离边界,支持程序集隔离、卸载及跨域通信,但.net core/5+已移除,由assemblyloadcontext替代。

AppDomain 在 .NET Framework 中确实能实现隔离、卸载、跨域通信,但在 .NET Core / .NET 5+ 中已被完全移除,没有替代 API。如果你正在开发新项目,别用它;如果维护老系统,得清楚它的边界和雷区。
AppDomain.CreateDomain 创建新域时路径参数怎么填
关键不是“怎么填”,而是不填错就成功了一半。最常出问题的是 appbasepath 和 apprelativesearchpath 混用或路径末尾多斜杠:
-
appbasepath必须是绝对路径,且目录必须存在,否则AppDomain.CreateDomain不报错但后续Assembly.Load会失败 -
apprelativesearchpath是相对于appbasepath的子目录名(不是完整路径),例如设为"plugins",则运行时会去appbasepath + @"\plugins\"查找私有程序集 - 不要手动拼接路径字符串,用
Path.Combine(root.BaseDirectory, "MyPlugins"),避免@"C:\MyApp\plugins\" + @"\sub\"导致双反斜杠或 UNC 路径解析异常 - 若只加载 GAC 或当前域已加载的程序集,
appbasepath可设为AppDomain.CurrentDomain.BaseDirectory,但此时隔离性几乎为零
AppDomain.UnhandledException 捕获不到 WinForms 点击异常
这不是 bug,是设计如此:AppDomain.UnhandledException 根本不处理 Windows 消息循环派发的 UI 异常。WinForms 的 Button.Click、TextBox.KeyDown 等事件中抛出的未捕获异常,会被 Application.ThreadException 拦截,而不是 AppDomain.CurrentDomain.UnhandledException:
- 必须同时注册
Application.ThreadException(WinForms)或DispatcherUnhandledException(WPF) -
AppDomain.UnhandledException的e.IsTerminating为true,此时禁止调用MessageBox.Show或任何 UI 操作,否则可能死锁或崩溃 - 它也无法捕获
Task中未await且未.Wait()的异常,得靠TaskScheduler.UnobservedTaskException - 注册顺序无关紧要,但建议在
Application.EnableVisualStyles()之后、Application.Run()之前完成所有异常监听器注册
卸载 AppDomain 后对象引用还有效吗
无效,而且访问会直接抛 AppDomainUnloadedException,不是 null 或静默失败:
- 通过
CreateInstanceAndUnwrap返回的对象是代理(TransparentProxy),底层绑定到原 AppDomain 的生命周期 -
AppDomain.Unload(domain)返回后,该 domain 内所有线程终止、所有静态字段清空、所有托管对象标记为可回收——但代理对象本身还在当前域,只是“断连”了 - 对代理对象的任何成员访问(哪怕是只读属性),都会触发跨域调用,此时 runtime 检测到目标域已卸载,立即抛异常
- 没有“安全检查是否已卸载”的 API,唯一可靠做法是:卸载前明确丢弃所有对该域对象的引用,或用
try/catch(AppDomainUnloadedException)包裹访问逻辑(仅用于诊断,不建议用于流程控制)
AppDomain 的核心价值从来不是“功能丰富”,而是“进程内硬隔离+可卸载”。但这也意味着它无法绕过序列化限制、不能共享内存、跨域调用有性能开销。真正需要动态插件隔离的场景,现在更推荐 AssemblyLoadContext(.NET Core+)或进程级沙箱,而非死守 AppDomain。










