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

C# 控制台应用程序中的.NET全局异常处理程序,c#,.net,vb.net,exception-handling,console-application,C#,.net,Vb.net,Exception Handling,Console Application,问题:我想为控制台应用程序中未处理的异常定义一个全局异常处理程序。在asp.net中,可以在global.asax中定义一个,在windows应用程序/服务中,可以定义如下 AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler); 但是如何为控制台应用程序定义全局异常处

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

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);
但是如何为控制台应用程序定义全局异常处理程序?
currentDomain似乎无法工作(.NET 2.0)

编辑:

啊,愚蠢的错误。
在VB.NET中,需要在currentDomain前面添加“AddHandler”关键字,否则在IntelliSense中看不到未处理的异常事件

这是因为VB.NET和C#编译器处理事件的方式不同。

不,这是正确的方法。这正是它应该发挥的作用,也许你可以从中受益:

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.NoInLine)]属性。

您正在尝试的应该根据.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
    }
}
现在,您的捕获将处理未捕获的任何内容(在主线程中)。它可以是优雅的,如果你愿意,甚至可以重新启动,或者你可以让应用程序死掉并记录异常。如果你想做任何清理,你会在最后加一个。每个线程都需要自己的高级异常处理,类似于主线程


编辑以澄清BlueMonkMN指出的关于线程的观点,并在他的回答中详细说明。

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

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

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

哇,很抱歉这是winforms的问题,对于控制台应用程序中使用的任何线程,您都必须将其包含在try/catch块中。遇到未处理异常的后台线程不会导致应用程序结束。

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

当另一个线程抛出异常以在应用程序退出之前执行某些清理时,您可以收到通知,但据我所知,您不能从控制台应用程序收到通知,如果不在引发异常的线程上处理异常,而不使用一些模糊的兼容性选项使应用程序的行为与.NET 1.x类似,则强制应用程序继续运行。这段代码演示了如何通知主线程来自其他线程的异常,但仍然会不愉快地终止:

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

我刚刚继承了一个旧的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
注释标记而不是单引号,因为堆栈溢出似乎可以更好地处理
REM

语法高亮显示,但不幸的是,异常仍然可以抛出Main()块之外。这并不像你想象的那样是一个“包罗万象”。请看@Hans的回答。@Mike首先,我说他这样做是正确的,他可以在主要的地方试一试。我不知道为什么你(或其他人)在我同意汉斯的意见时投了我一票,而汉斯只是提供了另一个我不希望得到支票的答案。这是不公平的,如果不提供任何证据证明AppDomain UnhandledException进程可以捕获到的异常是Main中的try/catch无法捕获的,那么就说备选方案是错误的。我觉得在没有证明错误原因的情况下说某件事是错误的是不礼貌的,仅仅说它是错误的,并不意味着它是错误的。我已经发布了您要求的示例。如果没有,请负责并从Mike的旧答案中删除不相关的反对票。(没有个人兴趣,只是不喜欢看到这样的滥用制度。)但你仍然在玩他玩的“游戏”,只是玩得更糟,因为这纯粹是报复,而不是基于回答的质量。这不是解决问题的办法,只会让问题变得更糟。尤其是当你报复那些对你的答案有合理担忧的人时(正如我已经证明的那样)。哦,我还要补充一点,否决投票的目的不是为了那些“完全愚蠢或违反规则”的人,而是为了判断答案的质量。在我看来,为了“评论”提供答案的人而投反对票的答案比基于答案本身的内容而投反对票的答案更严重,无论投票是否准确。不要太过个人化。我实现了你在这里提出的建议,但我不想退出应用程序。我只想记录它,然后继续这个过程(没有
Console.ReadLine()
或任何其他程序流的干扰。但我得到的是一次又一次地引发异常。@Shahrooz Jefri:一旦你得到一个未经处理的异常,你就不能继续了。堆栈被弄乱了,这是终端。如果你有