【问题标题】:.NET - What's the best way to implement a "catch all exceptions handler".NET - 实现“捕获所有异常处理程序”的最佳方法是什么
【发布时间】:2008-10-20 19:35:54
【问题描述】:

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

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

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

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

【问题讨论】:

标签: c# .net exception exception-handling


【解决方案1】:

我刚刚玩过 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)。

【讨论】:

  • 处理 DispatcherUnhandledException 解决了我的问题,但忘了更新问题。谢谢回答
  • 我想公平地说,上面的代码让每个失败的线程都永远存在。 IE。迟早你会有大量的僵尸线程。我个人觉得这不是很有用。
  • 我同意。这产生了僵尸。遗憾的是,我接受了在处理程序退出后立即终止我的应用程序的替代方案。毕竟,在让死线程永远休眠之前,您可以向服务器发送报告,让用户完成手头的任务并优雅地退出(或重新启动应用程序)。
  • 这个解决方案非常错误,同时在某些情况下也非常必要:) +1
  • 如果之前已经设置了线程的名称,这将抛出:Thread.CurrentThread.Name = "Dead thread"。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 除了辅助线程、定时器线程和线程池线程的异常之外的所有内容!
    • 我同意,如果“处理异常”被理解为实际上做了一些事情来从中恢复,AppDomain 的“UnhandledException”是不够的。然而,最初的问题似乎只针对错误报告,而不是关于恢复。仅出于这个目的就足够了。
    • @Steven:如果线程池作业是通过 BeginInvoke 启动的,那么在调用 EndInvoke 时,该线程的任何异常都会被编组到调用线程。
    【解决方案4】:

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

    对于其他线程:

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

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

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

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

    【讨论】:

    • @StevenA.Lowe 没有关于 CALM 的完整源代码备份?
    【解决方案5】:

    对于 WinForms,也不要忘记附加到当前线程的未处理异常事件(尤其是在使用多线程时)。

    一些关于最佳实践的链接 here 和 here 和 here (probably the best exception handling article for .net)

    【讨论】:

    【解决方案6】:

    还有一个很酷的东西叫做ELMAH,它会记录Web 应用程序中发生的任何ASP.NET 错误。我知道您在询问 Winform App 解决方案,但我觉得这可能对任何需要在 Web 应用程序上使用此类东西的人有益。我们在我工作的地方使用它,它对调试非常有帮助(尤其是在生产服务器上!)

    以下是它的一些功能(直接从页面上拉下来):

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

    【讨论】:

    • 不管我问的是非网络应用程序,这对其他人来说仍然是非常有用的信息!所以+1
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

      我正在使用以下方法,该方法有效并大大减少了代码量(但我不确定是否有更好的方法或它可能存在哪些陷阱。 每当您致电时: 我怀疑那些给出减号的人会礼貌地澄清他们的行为; )

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

      这里是 ErrorHandler 代码: 只是为了明确 - : objUser - 是为应用用户建模的对象(您可能会获得诸如域名、部门、​​地区等信息以用于日志记录 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
      

      【讨论】:

      • 这只会捕获当前线程上的异常。 IE。如果 CallTheCodeThatMightThrowException 创建或使用其他线程,它几乎没有用。
      【解决方案9】:

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

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

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

      Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
      

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

      Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
      

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

      【讨论】:

        猜你喜欢
        • 2021-08-07
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 2015-07-16
        • 2011-04-29
        • 1970-01-01
        • 2022-08-03
        相关资源
        最近更新 更多