.NET – 什么是实现“捕获所有exception处理程序”的最佳方式
我想知道什么是最好的办法是“如果一切都失败了”。
我的意思是,在应用程序中尽可能多地处理exception,但仍然一定会出现错误,所以我需要有一些能够捕获所有未处理的exception的东西,以便我可以收集信息并将它们存储在数据库中或提交它们到networking服务。
AppDomain.CurrentDomain.UnhandledException事件是否捕获所有内容? 即使应用程序是multithreading的?
附注:Windows Vista公开了原生API函数,允许任何应用程序在崩溃后自行恢复…现在不能想到名称…但我宁愿不使用它,因为许多用户仍在使用Windows XP。
我刚刚玩AppDomain的UnhandledException行为,(这是未处理的exception注册的最后一个阶段)
是的,处理事件处理程序后,您的应用程序将被终止,并显示令人讨厌的“…程序停止工作对话框”。
:)你仍然可以避免这一点。
查看:
class Program { void Run() { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Console.WriteLine("Press enter to exit."); do { (new Thread(delegate() { throw new ArgumentException("ha-ha"); })).Start(); } while (Console.ReadLine().Trim().ToLowerInvariant() == "x"); Console.WriteLine("last good-bye"); } int r = 0; void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Interlocked.Increment(ref r); Console.WriteLine("handled. {0}", r); Console.WriteLine("Terminating " + e.IsTerminating.ToString()); Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Name = "Dead thread"; while (true) Thread.Sleep(TimeSpan.FromHours(1)); //Process.GetCurrentProcess().Kill(); } static void Main(string[] args) { Console.WriteLine("..."); (new Program()).Run(); } }
PS不要在较高级别处理未处理的Application.ThreadException(WinForms)或DispatcherUnhandledException(WPF)。
在ASP.NET中,您可以使用Global.asax
文件中的Application_Error
函数。
在WinForms中,您使用ApplicationEvents
文件中的MyApplication_UnhandledException
如果在您的代码中发生未处理的exception,则会调用这两个函数。 您可以loggingexception,并从这些函数向用户显示一条好消息。
对于Winform应用程序,除了AppDomain.CurrentDomain.UnhandledException外,我还使用Application.ThreadException和Application.SetUnhandledExceptionMode (w / UnhandledExceptionMode.CatchException)。 这个组合似乎抓住了一切。
在主线程上,您有以下选项:
- 控制台或服务应用程序:
AppDomain.CurrentDomain.UnhandledException
- WinForms应用程序:
Application.ThreadException
- Web应用程序:Global.asax的
Application_Error
对于其他线程:
- 次线程没有未处理的exception; 使用SafeThread
- 工作者线程:(计时器,线程池)根本没有安全网!
请记住,这些事件不处理exception,他们只是将它们报告给应用程序 – 往往是为时太晚,没有做任何有用/理智的事情
loggingexception情况良好,但监视应用程序更好;-)
警告:我是SafeThread文章的作者。
对于WinForms,不要忘记附加到当前线程的未处理的exception事件(特别是如果您使用multithreading)。
这里和这里和这里的最佳实践的一些链接(可能是最好的.netexception处理文章)
还有一个很酷的东西叫ELMAH ,它将logging在Web应用程序中发生的任何ASP.NET错误。 我知道你在问一个Winform应用程序的解决scheme,但是我觉得这对任何需要这种types的东西的人来说都是有益的。 我们在我工作的地方使用它,并且在debugging时非常有帮助(特别是在生产服务器上)!
这里有一些function(从页面中拉出):
- logging几乎所有未处理的exception。
- 一个网页来远程查看重新编码的例外的整个日志。
- 一个网页,远程查看任何一个logging的exception的全部细节。
- 在很多情况下,即使closures了customErrors模式,也可以查看ASP.NET为给定exception生成的原始黄色死亡屏幕。
- 每次发生错误的电子邮件通知。
- 来自日志的最近15个错误的RSS源。
- 日志的许多后备存储实现,包括内存中的Microsoft SQL Server和几个社区贡献的实现。
即使在multithreading应用程序中,您也可以监视该处理程序中的大多数exception,但.NET(从2.0开始)将不允许取消未处理的exception,除非启用了1.1兼容性模式。 当发生这种情况时,AppDomain将被closures,无论如何。 最好的办法是在不同的AppDomain中启动应用程序,这样你就可以处理这个exception,并创build一个新的AppDomain来重新启动应用程序。
我正在使用下面的方法,它可以大大减less代码的数量(但是我不确定是否有更好的方法,或者它的缺陷是什么)每当你打电话给我时,足以说明他们的行为;)
try { CallTheCodeThatMightThrowException() } catch (Exception ex) { System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace (); Utils.ErrorHandler.Trap ( ref objUser, st, ex ); } //eof catch
这里是ErrorHandler代码:只是为了清楚:objUser – build模appusers的对象(你可能会得到诸如域名,部门,地区等信息用于logging目的ILoglogging器 – 是日志logging对象 – 例如执行日志logging活动StackTrace st – StackTrace对象,为您的应用程序提供debugging信息
using System; using log4net; //or another logging platform namespace GenApp.Utils { public class ErrorHandler { public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex ) { if (ex is NullReferenceException) { //do stuff for this ex type } //eof if if (ex is System.InvalidOperationException) { //do stuff for this ex type } //eof if if (ex is System.IndexOutOfRangeException) { //do stuff for this ex type } //eof if if (ex is System.Data.SqlClient.SqlException) { //do stuff for this ex type } //eof if if (ex is System.FormatException) { //do stuff for this ex type } //eof if if (ex is Exception) { //do stuff for this ex type } //eof catch } //eof method }//eof class } //eof namesp
在一个更改的GUI应用程序中,默认情况下,源自GUI线程的exception由分配给Application.ThreadException的任何内容处理。
源自其他线程的exception由AppDomain.CurrentDomain.UnhandledException处理。
如果你想让你的GUI线程exception像你的非GUI一样工作,以便它们能够被AppDomain.CurrentDomain.UnhandledException处理,你可以这样做:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
使用ThreadException捕获GUI线程exception的一个好处是,您可以使用让应用程序继续的选项。 为了确保没有configuration文件覆盖默认行为,你可以调用:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
您仍然容易受到performance不佳的本地dll的例外。 如果本地dll使用Win32 SetUnhandledExceptionFilter安装自己的处理程序,则应该将指针保存到上一个filter并调用它。 如果不这样做,你的处理程序将不会被调用。