.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并调用它。 如果不这样做,你的处理程序将不会被调用。