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


198

问题:我想在控制台应用程序中为未处理的异常定义全局异常处理程序。在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#编译器对事件处理的处理不同。

Answers:


283

不,那是正确的方法。这完全可以正常工作,也许您可​​以从以下方面进行工作:

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)]属性应用于该方法。


3
我实现了您在此处提出的建议,但是我不想退出该应用程序。我只是想记录它,并继续处理(不包括Console.ReadLine()或程序流的任何其他干扰但我得到的是例外,一次又一次重新认识,并再次。

3
@Shahrooz Jefri:一旦遇到未处理的异常,您将无法继续。堆栈被弄乱了,这是终端。如果您有服务器,则在UnhandledExceptionTrapper中可以执行的操作是使用相同的命令行参数重新启动程序。
Stefan Steiger 2013年

6
当然可以!我们在这里不是在谈论Application.ThreadException事件。
汉斯·帕桑

4
我认为理解此答案和回复注释的关键是要意识到该代码现在可以检测到异常,但并不能像在普通try / catch块中那样完全“处理”该异常,您可以选择继续。请参阅我的其他答案以获取详细信息。如果您以这种方式“处理”异常,那么当另一个线程上发生异常时,您将无法继续运行。从这个意义上讲,如果用“处理”来表示“处理并继续运行”,则可以说该答案未完全“处理”异常。
BlueMonkMN 2014年

4
更不用说有很多技术可以在不同的线程中运行。例如,任务并行库(TPL)有其自己的方式来捕获未处理的异常。因此,说这不适用于所有情况有点荒谬,在C#中没有一个地方可以万事俱备,但是根据您使用的技术,您可以使用各种各样的地方。
Doug

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");
   }
}

TRY CATCH在发布模式下
无法解决

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块中。遇到未处理的异常的后台线程不会导致应用程序结束。


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


-13

您要尝试的操作应根据.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将处理未捕获的任何内容(在主线程中)。它可以很优美,甚至可以根据需要重新启动,也可以让应用程序死掉并记录异常。如果您想进行任何清理,则将添加一个final。 每个线程都将需要自己的类似于主线程的高级异常处理。

编辑以阐明BlueMonkMN所指出并在其答案中详细显示的关于线程的观点。


1
不幸的是,实际上仍然可以将异常抛出到Main()块之外。这实际上并不是您想的那样“包罗万象”。参见@汉斯的答案。
Mike Atlas 2010年

@迈克首先,我说他的做法是正确的,而且他可以尝试尝试/接住。我不确定为什么当我同意汉斯的意见时,您(或其他人)否决了我的意见,而只是提供了我不希望得到检查的答案。那不是很公平,然后说替代方案是错误的,而没有提供任何证据来证明AppDomain UnhandledException进程如何捕获Main中的try / catch无法捕获的异常。我发现在不证明错误原因的情况下说出错误是很不礼貌的,只是说出是错,而不是说错。
Rodney S. Foley

5
我已经发布了您要的示例。如果没有,请负责并从迈克的旧答案中删除无关的否决票。(没有个人利益,只是不喜欢看到这种系统滥用。)
BlueMonkMN 2010年

3
但是,您仍然玩他所玩的“游戏”,只是以一种更糟糕的方式,因为这是纯粹的报复行为,而不是基于答案的质量。那不是解决问题的方法,只会使问题变得更糟。当您对甚至对您的答案有合理担忧的人进行报复时,这尤其糟糕(如我所证明的那样)。
BlueMonkMN 2010年

3
哦,我还要补充一点,不赞成投票的对象不是“完全是白痴或违反规则”的人,而是要判断答案的质量。在我看来,与基于答案本身的内容而对答案进行否决而不管该投票是否正确相比,对其进行否决投票以对提供该信息的人进行“评论”是一种更大的滥用。不要把它做得如此私人。
BlueMonkMN 2010年
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.