.NET控制台应用程序中的全局异常处理程序
.NET控制台应用程序中的全局异常处理程序
问题:我想为控制台应用程序定义全局异常处理程序来处理未处理的异常。 在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#编译器处理事件处理方式不同。
如果你有一个单线程应用程序,你可以在Main函数中使用简单的try/catch,然而,这并不能涵盖可能在其他线程上抛出的异常,例如(如其他评论中所指出的)。这段代码演示了即使你在Main函数中尝试处理异常,异常仍然可能导致应用程序终止(请注意,如果你在异常发生之前按下回车键并允许应用程序正常退出,程序会很优雅地退出,但如果你让它继续运行,它会非常不幸地终止):
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"); } }
不,这是正确的做法。这正好按照你的意愿工作,你可以从这里开始练习:
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); } }
请注意,您无法通过这种方式捕获由JIT生成的类型和文件加载异常。它们会在Main()方法开始运行之前发生。要捕获这些异常,需要延迟JIT,在另一个方法中移动风险代码,并将[MethodImpl(MethodImplOptions.NoInlining)]属性应用于该方法。