在模块化表单应用程序中使用 AppDomain.UnhandledException
我目前正在开发一个完全模块化的 .NET 应用程序,其中:
- 有一个加载模块 DDL 的主机应用程序。模块 DDL 具有可以多次打开的形式。
- 您可以卸载应用程序域。这将导致模块的所有打开的窗体被关闭。
我想要做的是,当模块中发生未处理的异常时,应用程序应该卸载该模块的 DDL 并防止主机崩溃。
我尝试使用 AppDomain.UnhandledException 事件,该事件在创建后立即分配了一个处理程序 AppDomain 如下:
// Creates the ApplicationDomain
this._applicationDomain = AppDomain.CreateDomain(this.AppDomainName);
this._applicationDomain.UnhandledException += new UnhandledExceptionEventHandler(_applicationDomain_UnhandledException);
问题是:处理程序从未被调用。微软此处表示
例如,假设一个线程在应用程序域“AD1”中启动,调用应用程序域“AD2”中的方法,然后从那里调用应用程序域“AD3”中的方法,并在该方法中引发异常。可以引发 UnhandledException 事件的第一个应用程序域是“AD1”。如果该应用程序域不是默认应用程序域,则也可以在默认应用程序域中引发该事件。
即使在分配处理程序之后,异常仍然会转到
Application.Run(new HostForm());
我想,如果我为每个模块的表单创建一个新的消息循环,它就会起作用,因为这样运行表单的线程将是一个单独的线程,但这似乎是一个愚蠢的解决方案。
我的另一个想法是使用默认应用程序域的 AppDomain.UnhandledException 事件,但是我怎样才能:
- 发现原始的 AppDomain 以及要卸载的模块?
- 防止应用程序死亡? (我尝试这样做,e.IsTerminate具有真正的价值,并且异常仍然被捕获在
任何人?拜托,我真的需要它。
I am currently developing a .NET application completely modular, in which:
- There is the host applicaiton that loads module DDLs. The module DDLs have forms that can be opened several times.
- You can unload the application domain. This will cause all the opened forms of the module to be closed.
What I want to do is to, when an unhandled exception happens in the module, the application should unload the DDL of that module and prevent the Host to crash.
I tried to use the AppDomain.UnhandledException Event, which has a handler assigned to it right after the creation of the
AppDomain as follows:
// Creates the ApplicationDomain
this._applicationDomain = AppDomain.CreateDomain(this.AppDomainName);
this._applicationDomain.UnhandledException += new UnhandledExceptionEventHandler(_applicationDomain_UnhandledException);
The problem is: that the handler is never called. Microsoft says here that
For example, suppose a thread starts in application domain "AD1", calls a method in application domain "AD2", and from there calls a method in application domain "AD3", where it throws an exception. The first application domain in which the UnhandledException event can be raised is "AD1". If that application domain is not the default application domain, the event can also be raised in the default application domain.
Even after I assign the handler, the exception still goes to
Application.Run(new HostForm());
I suppose that, if I created a new message loop for each module's form it would work, because then the thread in which the form would be running would be a separated thread, but it seems to be a stupid solution.
Another idea I had was to use than the AppDomain.UnhandledException Event of the default application domain, but how can I then:
- Discover the AppDomain of origin and therefore the module to unload?
- Prevent the Application to die? (I tried doing this and the e.IsTerminating comes with true value and the exception is still caught in the
Anyone? Please, I really need it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
关于为什么 AppDomain.CurrentDomain.UnhandledException 处理程序不抑制未处理的异常,有一个相当好的解释(带有支持链接)此处。您链接到的 MSDN 文章也在您引用的部分下方描述了这种行为。
因此,即使您可以触发该事件,也不太可能满足您的要求,即应用程序不会很快终止。 UnhandledException 处理程序实际上只是在应用程序终止之前为您提供最后一次记录重要信息的机会。
最具弹性的应用程序(例如 Google Chrome)将使用完整的进程隔离来确保插件中出现的任何问题都会破坏外部插件主机,而不是主应用程序。我自己使用这种机制来创建一个与您所描述的类似的应用程序,甚至在每个外部插件主机内创建沙盒AppDomain,以确保在主应用程序提升时插件无法删除关键文件。
此处有一篇非常好的文章,详细描述了隔离技术并重申了这一点由于 UnhandledException 问题,AppDomain 插件系统不是最佳选择。如果您认真对待容错插件架构,我建议您实现上一篇文章中所述的完整进程隔离。
There's a reasonably good explanation (with supporting links) about why the AppDomain.CurrentDomain.UnhandledException handler doesn't suppress unhandled exceptions here. The MSDN article you linked to also describes this behaviour just below the part you quoted.
So even if you could get the event to fire, it isn't likely to satisfy your requirement that the application not be terminated shortly afterwards. The UnhandledException handler is really only there to give you one last chance to log important information before the application is terminated.
The most resilient applications (Google Chrome for instance) will use full process isolation to ensure anything that goes wrong in a plugin tears down the external plugin host, not the main application. I've used this mechanism myself to create an application similar to the one you describe, and even went to the extent of creating sandboxed AppDomain's inside each external plugin host to ensure plugins couldn't delete critical files if the main application was elevated.
There's a really good article here that describes isolation techniques in detail and reiterates that the AppDomain plugin system isn't the best option due to the UnhandledException problem. If you're serious about a fault tolerant plugin architecture I'd recommend implementing full process isolation as described in that last article.