关于控制台应用程序中的c#:. NET Global异常处理程序

.NET Global exception handler in console application

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

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

但是,如何为控制台应用程序定义全局异常处理程序?< BR>当前域似乎不工作(.net 2.0)?

编辑:

啊,愚蠢的错误。
在vb.net中,需要在currentdomain前面添加"addhandler"关键字,否则在intellisense中看不到unhandledException事件…< BR>这是因为vb.net和c编译器处理事件的方式不同。


不,这是正确的方法。这是完全可行的,您可以从以下方面着手:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)]属性应用到它。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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中的行为类似的选项。此代码演示了如何向主线程通知来自其他线程的异常,但仍将不愉快地终止:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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;
   }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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");
   }
}


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

1
2
3
4
5
6
7
8
static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

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

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


您所尝试的应该根据.NET 2.0的msdn文档工作。您还可以在控制台应用程序的入口点周围尝试一次"尝试/捕获"操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

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

根据BlueMonkmn所指出的并在其答案中详细说明的,对线程的观点进行了编辑以澄清。