76

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

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

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

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

4

9 回答 9

34

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

于 2009-06-28T22:43:29.163 回答
18

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

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

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

于 2008-10-20T19:39:07.303 回答
13

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

于 2008-10-20T19:48:48.813 回答
6

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

对于其他线程:

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

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

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

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

于 2008-10-20T20:06:59.167 回答
4

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

关于最佳实践的一些链接herehere以及here(可能是.net的最佳异常处理文章)

于 2008-10-20T19:47:47.987 回答
2

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

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

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

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

于 2008-10-20T21:51:41.107 回答
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
于 2009-05-20T08:47:00.300 回答
0

在托管的 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 安装自己的处理程序,它应该保存指向前一个过滤器的指针并也调用它。如果它不这样做,您的处理程序将不会被调用。

于 2009-09-26T23:01:12.383 回答