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


75

我想知道最好的方法是“如果所有其他方法都失败了,那就赶上它”。

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

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

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


2
这是Windows Vista中的“重新启动管理器”功能:danielmoth.com/Blog/2006/10/vista-restart-manager.html
huseyint


Answers:


33

我刚刚玩过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();
    }
}

PS请在更高级别处理未处理的Application.ThreadException(WinForms)或DispatcherUnhandledException(WPF)。


处理DispatcherUnhandledException解决了我的问题,但是却忘记更新问题。Thnx的答案
TimothyP 2009年

21
我想可以公平地说,上面的代码使每个失败的线程永远存在。即迟早会有大量的僵尸线程挂在身边。就个人而言,我认为这不是很有用。
布赖恩·拉斯穆森

2
我同意。那产生了僵尸。不幸的是,我接受这种选择,而不是在处理程序退出后立即终止我的应用程序。毕竟,在让死线程永久休眠之前,您可以向服务器发送报告,让用户完成手头的任务并优雅地退出(或重新启动应用程序)。
bohdan_trotsenko,2009年

3
这种解决方案是如此错误,同时在某些情况下也有必要:) +1
empi

1
如果先前已经设置了线程的名称,则会抛出此异常Thread.CurrentThread.Name = "Dead thread"
FunctorSalad

17

在ASP.NET中,您可以Application_ErrorGlobal.asax文件中。

在的WinForms,您使用MyApplication_UnhandledExceptionApplicationEvents文件中使用

如果您的代码中发生未处理的异常,则将调用这两个函数。您可以记录这些异常,并通过这些功能向用户显示好消息。


13

对于Winform应用程序,除了AppDomain.CurrentDomain.UnhandledException外,我还使用Application.ThreadExceptionApplication.SetUnhandledExceptionMode(带有UnhandledExceptionMode.CatchException)。这种结合似乎可以抓住一切。


6
除了辅助线程,计时器线程和线程池线程上的异常以外的所有内容!
史蒂文·劳

2
我同意,如果将“处理异常”理解为实际上是要从中恢复的事情,那么AppDomain的“ UnhandledException”是不够的。但是,最初的问题似乎只针对错误报告,而不是针对恢复。仅出于该目的,就足够了。
Christian.K,

1
@Steven:如果线程池作业是通过BeginInvoke启动的,则在调用EndInvoke时,该线程的任何异常都会编组到调用线程。
布赖恩·拉斯穆森

6

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

对于其他线程:

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

请记住,这些事件不会处理异常,它们只会报告向应用程序-通常在为时不晚对它们执行任何有用的处理

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

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


@ StevenA.Lowe是否无法完整备份有关CALM的源代码?
Kiquenet


2

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

它具有一些功能(直接从页面上拉出):

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

不管我问了非Web应用程序,这对于其他人来说仍然是非常有用的信息!+1
TimothyP'3

2

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


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 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

这只会捕获当前线程上的异常。也就是说,如果CallTheCodeThatMightThrowException创建或使用其他线程,则几乎没有用。
布赖恩·拉斯穆森

0

在托管的GUI应用程序中,默认情况下,GUI线程中产生的异常由分配给Application.ThreadException的对象处理。

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

如果希望您的GUI线程异常像您的非GUI异常一样工作,以使它们由AppDomain.CurrentDomain.UnhandledException处理,则可以执行以下操作:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

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

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

您仍然容易受到行为不良的本机dll导致的异常的攻击。如果本机dll使用Win32 SetUnhandledExceptionFilter安装了自己的处理程序,则应该保存指向先前过滤器的指针并对其进行调用。如果不这样做,您的处理程序将不会被调用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.