首页 文章

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

提问于
浏览
72

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

我的意思是,你在应用程序中处理尽可能多的异常,但仍然存在bug,所以我需要有一些东西可以捕获所有未处理的异常,这样我就可以收集信息并将它们存储在数据库中或提交它们到网络服务 .

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

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

9 回答

  • 16

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

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

  • 32

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

  • 4

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

    对于其他线程:

    • 辅助线程没有未处理的异常;使用SafeThread

    • 工作者线程:(计时器,线程池)根本没有安全网!

    请记住,这些事件不会处理异常,它们只是将它们报告给应用程序 - 通常在对它们做任何有用/明智的事情为时已晚 .

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

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

  • 13

    还有一个名为ELMAH的很酷的东西,它会记录Web应用程序中发生的任何ASP.NET错误 . 我知道你're asking about a Winform App solution, but I felt this could be beneficial to anyone needing this type of thing on a web app. We use it where I work and it'在调试方面非常有帮助(特别是在 生产环境 服务器上!)

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

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

  • 2

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

    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 logger - 是日志对象 - 例如一个执行日志记录活动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
    
  • 2

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

  • 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安装自己的处理程序,则应该将指针保存到前一个过滤器并调用它 . 如果不这样做,你的处理程序就不会被调用 .

  • 0

    我刚刚玩过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)的未处理 .

  • 6

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

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

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

相关问题