控制台应用程序中的.NET Global异常处理程

时间:2010-06-28 14:25:59

标签: c# .net vb.net exception-handling console-application

问题:我想在控制台应用程序中为未处理的异常定义一个全局异常处理程序。在asp.net中,可以在global.asax中定义一个,在windows应用程序/服务中,可以定义如下

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

但是如何为控制台应用程序定义全局异常处理程序呢? currentDomain似乎不起作用(.NET 2.0)?

修改

唉,愚蠢的错误。
在VB.NET中,需要在currentDomain前添加“AddHandler”关键字,否则在IntelliSense中看不到UnhandledException事件...
那是因为VB.NET和C#编译器以不同的方式处理事件处理。

5 个答案:

答案 0 :(得分:259)

不,这是正确的方法。这完全符合它的要求,你可以从中工作:

using System;

class Program {
    static void Main(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

请记住,您无法通过这种方式捕获由抖动生成的类型和文件加载异常。它们发生在Main()方法开始运行之前。捕获这些需要延迟抖动,将危险代码移动到另一个方法并将[MethodImpl(MethodImplOptions.NoInlining)]属性应用于它。

答案 1 :(得分:23)

如果你有一个单线程应用程序,你可以在Main函数中使用一个简单的try / catch,但是,这不包括可能在其他线程上抛出Main函数之外的异常(例如在其他评论中指出)。此代码演示了异常如何导致应用程序终止,即使您尝试在Main中处理它(注意如果按Enter键并允许应用程序在异常发生之前正常退出,程序将如何正常退出,但是如果您让它运行,它非常不幸地终止了):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

您可以在应用程序退出之前收到另一个线程抛出异常以执行某些清理的通知,但据我所知,如果您不这样做,则无法从控制台应用程序强制应用程序继续运行处理抛出它的线程上的异常,而不使用一些不起眼的兼容性选项,使应用程序的行为与.NET 1.x相同。此代码演示了如何通知主线程来自其他线程的异常,但仍然会不幸终止:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

所以在我看来,在控制台应用程序中处理它的最干净方法是确保每个线程在根级别都有一个异常处理程序:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}

答案 2 :(得分:12)

您还需要处理线程中的异常:

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

哎呀,抱歉是winforms,对于你在控制台应用程序中使用的任何线程,你必须将它们包含在try / catch块中。遇到未处理异常的后台线程不会导致应用程序结束。

答案 3 :(得分:1)

我刚刚继承了一个旧的VB.NET控制台应用程序,并且需要设置一个全局异常处理程序。既然这个问题多次提到VB.NET并用VB.NET标记,但是这里所有其他答案都在C#中,我想我也会为VB.NET应用程序添加确切的语法。

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

我在这里使用了REM注释标记而不是单引号,因为Stack Overflow似乎可以更好地处理REM突出显示的语法。

答案 4 :(得分:-9)

您正在尝试的应该根据.Net 2.0的MSDN文档工作。您还可以在控制台应用程序的入口点周围尝试使用try / catch。

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

现在你的catch会处理任何未捕获的东西(在主线程中)。它可以是优雅的,甚至可以重新启动它,如果你想要的话,或者你可以让应用程序死掉并记录异常。如果你想做任何清理,你最后添加一个。 每个线程都需要自己的高级异常处理,类似于main。

编辑澄清BlueMonkMN指出的有关线程的观点,并在答案中详细说明。