.NET - 实现“捕获所有异常处理程序”的最佳方法是什么

时间:2008-10-20 19:35:55

标签: c# .net exception exception-handling

我想知道最好的方法是“如果所有其他方法都失败了”。

我的意思是,您在应用程序中处理尽可能多的异常, 但仍然有一定的bug,所以我需要有一些东西 捕获所有未处理的异常,以便我可以收集信息和存储 它们在数据库中或提交给Web服务。

AppDomain.CurrentDomain.UnhandledException事件是否捕获了所有内容? 即使应用程序是多线程的?

附注:Windows Vista公开了允许任何应用程序的本机API函数 在崩溃后自我恢复......现在想不到这个名字......但我宁愿不这样做 使用它,因为我们的许多用户仍在使用Windows XP。

9 个答案:

答案 0 :(得分:32)

我刚刚玩过AppDomain的UnhandledException行为, (这是未处理的异常在注册的最后阶段)

是的,在处理完事件处理程序后,您的应用程序将被终止并显示令人讨厌的“...程序停止工作对话框”。

:) 你仍然可以避免这种情况。

退房:

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();
    }
}

P.S。在较高级别处理Application.ThreadException(WinForms)或DispatcherUnhandledException(WPF)的未处理。

答案 1 :(得分:16)

在ASP.NET中,您使用Application_Error文件中的Global.asax函数。

在WinForms中,您使用MyApplication_UnhandledException文件中的ApplicationEvents

如果代码中出现未处理的异常,则会调用这两个函数。您可以记录异常并从这些函数向用户显示一条好消息。

答案 2 :(得分:13)

对于Winform应用程序,除了AppDomain.CurrentDomain.UnhandledException之外,我还使用Application.ThreadExceptionApplication.SetUnhandledExceptionMode(w / UnhandledExceptionMode.CatchException)。这种组合似乎抓住了一切。

答案 3 :(得分:6)

在主线程上,您有以下选项:

对于其他主题:

  • 辅助线程没有未处理的异常;使用SafeThread
  • 工作线程:(计时器,线程池)根本没有安全网!

请记住,这些事件不会处理异常,它们只是向应用程序报告 - 通常在做任何有用的事情为时已晚关于他们

记录异常是好的,但监控应用程序更好; - )

警告:我是SafeThread文章的作者。

答案 4 :(得分:4)

对于WinForms,不要忘记附加到当前Thread的未处理异常事件(特别是如果您使用多线程)。

有关最佳做法herehere以及here (probably the best exception handling article for .net)

的一些链接

答案 5 :(得分:2)

还有一个名为ELMAH的很酷的东西,它会记录Web应用程序中发生的任何ASP.NET错误。我知道你在询问Winform App解决方案,但我觉得这对于在网络应用上需要这种类型的任何人都有好处。我们在工作的地方使用它,它在调试方面非常有用(特别是在生产服务器上!)

这里有一些功能(从页面上拉出来):

  
      
  • 记录几乎所有未处理的例外情况。
  •   
  • 远程查看已重新编码的例外日志的网页。
  •   
  • 用于远程查看任何一个已记录异常的完整详细信息的网页。
  •   
  • 在很多情况下,你可以查看死亡的原始黄色屏幕   为给定的ASP.NET生成的   异常,即使使用customErrors模式   关掉了。
  •   
  • 发生时每个错误的电子邮件通知。
  •   
  • 日志中最近15个错误的RSS提要。
  •   
  • 日志的许多后备存储实现,包括   内存,Microsoft SQL Server和   几个由社区贡献。
  •   

答案 6 :(得分:2)

即使在多线程应用程序中,您也可以监视该处理程序中的大多数异常,但.NET(从2.0开始)将不允许您取消未处理的异常,除非您启用1.1兼容模式。当发生这种情况时,AppDomain将被关闭,无论如何。您可以做的最好的事情是在不同的AppDomain中启动应用程序,以便您可以处理此异常并创建一个新的AppDomain来重新启动应用程序。

答案 7 :(得分:0)

我正在使用以下方法,它可以工作并大大减少代码量(但我不确定是否有更好的方法或它的缺陷可能是什么。 每当你打电话: 我认为给予弊端的态度会礼貌地澄清他们的行为; )

try 
{
    CallTheCodeThatMightThrowException()
 }
catch (Exception ex)
{
    System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
    Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch

这是ErrorHandler代码: 只是为了弄清楚 - :objUser - 是为appusers建模的对象(你可能会得到诸如域名,部门,区域等信息用于记录目的 ILog记录器 - 是记录对象 - 例如执行日志记录活动的人 StackTrace st - StackTrace对象为您提供应用程序的调试信息

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

答案 8 :(得分:0)

在magedd GUI应用程序中,默认情况下,源自GUI线程的异常由分配给Application.ThreadException的任何内容处理。

源自其他线程的异常由AppDomain.CurrentDomain.UnhandledException处理。

如果您希望GUI线程异常与非GUI GUI一样工作,以便它们由AppDomain.CurrentDomain.UnhandledException处理,您可以这样做:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

使用ThreadException捕获GUI线程异常的一个好处是,您可以使用让应用程序继续运行的选项。要确保没有配置文件覆盖默认行为,您可以调用:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

您仍然容易受到行为不端的原生dll的异常攻击。如果本机dll使用Win32 SetUnhandledExceptionFilter安装自己的处理程序,则应该将指针保存到前一个过滤器并调用它。如果不这样做,你的处理程序就不会被调用。