在WPF应用程序中全局捕获exception?

我们有一个WPF应用程序,其中的一部分可能在运行时抛出exception。 我想在全球范围内捕捉任何未处理的exception并将其logging下来,否则继续执行程序,就好像什么也没发生一样(有点像VB的On Error Resume Next )。

这在C#中可能吗? 如果是这样的话,我需要把exception处理代码放在哪里?

目前我看不到任何单一的地方,我可以包装一个try / catch ,并捕获所有可能发生的exception。 即使这样,我也不会因为这个问题而放弃任何已经被处决的事情。 还是我在这里考虑可怕的错误方向?

ETA:因为很多人指出:应用不是用来控制核电站的。 如果它崩溃,这不是什么大不了的事情,但大多数与UI相关的随机exception在它将被使用的上下文中是令人讨厌的。 那里有(也可能还有)其中一些,因为它使用插件架构,可能会被其他人(也就是在这种情况下的学生;所以没有经验丰富的开发人员能够编写完全无差错的代码)进行扩展。

至于被捕获的exception:我将它们logging到日志文件中,包括完整的堆栈跟踪。 那是这个练习的重点。 只是为了对付那些把我的比喻成VB的OERN字面的人。

我知道,盲目地忽略某些类别的错误是危险的,可能会破坏我的应用程序实例。 如前所述,这个程序对任何人都不是任务关键的。 没有一个正确的头脑会把人类文明的生存下去。 这只是testing某些devise方法的一个小工具。 软件工程。

对于应用程序的直接使用,在例外情况下可能不会发生很多事情:

  • 没有exception处理 – 错误对话框和应用程序退出。 实验必须重复,尽pipe可能与另一个主题。 没有错误logging,这是不幸的。
  • 一般exception处理 – 良性错误被困,没有造成伤害。 这应该是从我们在开发过程中看到的所有错误判断的常见情况。 忽视这种错误不应该有直接的后果。 核心数据结构经过了充分的testing,可以轻松实现。
  • 一般的exception处理 – 严重的错误被困,可能会在稍后崩溃。 这可能很less发生。 到目前为止,我们从未见过。 无论如何这个错误是logging的,崩溃可能是不可避免的。 所以这在概念上类似于第一种情况。 除了我们有一个堆栈跟踪。 在大多数情况下,用户甚至不会注意到。

至于程序产生的实验数据:最严重的错误只会导致没有数据被logging。 微妙的变化改变了实验的结果,所以很less发生。 即使在这种情况下,如果结果看起来可疑,logging的错误; 如果它是一个总exception值,仍然可以丢弃该数据点。

总结一下:是的,我认为自己仍然至less有一部分是理智的,我不认为全局的exception处理程序会导致程序运行必然是完全邪恶的。 如前所述,这样的决定可能是有效的,这取决于应用程序。 在这种情况下,它被判断为一个有效的决定,而不是完全和彻底的废话。 对于任何其他应用程序,这个决定可能看起来不同 但是请不要指责我或者其他从事这个项目的人有可能因为我们忽略了错误而炸毁世界。

注意:该应用程序只有一个用户。 它不像Windows或Office那样被数百万人所使用,其中exception冒泡的成本根本就已经大不相同了。

使用Application.DispatcherUnhandledException Event 。 看到这个问题的总结(见Drew Noakes的答案 )。

请注意,仍然会有例外情况导致应用程序无法成功恢复,例如在尝试保存到数据库时出现堆栈溢出,内存耗尽或networking连接丢失等情况。

AppDomain.UnhandledException事件

此事件提供未捕获exception的通知。 它允许应用程序在系统默认处理程序向用户报告exception并终止应用程序之前logging有关该exception的信息。

  public App() { AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler); } static void MyHandler(object sender, UnhandledExceptionEventArgs args) { Exception e = (Exception) args.ExceptionObject; Console.WriteLine("MyHandler caught : " + e.Message); Console.WriteLine("Runtime terminating: {0}", args.IsTerminating); } 

如果在默认的应用程序域中处理了UnhandledException事件,那么在任何线程中都会引发任何未处理的exception,而不pipe该线程在哪个应用程序域中启动。如果线程在具有UnhandledException的事件处理程序的应用程序域中启动,该事件在该应用程序域中引发。 如果该应用程序域不是默认应用程序域,并且在默认应用程序域中还有一个事件处理程序,则在两个应用程序域中都会引发该事件。

例如,假设线程在应用程序域“AD1”中启动,调用应用程序域“AD2”中的方法,并从那里调用应用程序域“AD3”中的方法,在那里引发exception。 可以引发UnhandledException事件的第一个应用程序域是“AD1”。 如果该应用程序域不是默认的应用程序域,则该事件也可以在默认的应用程序域中引发。

另外还有其他人在这里提到,请注意将Application.DispatcherUnhandledException (及其类似者 )与

 <configuration> <runtime> <legacyUnhandledExceptionPolicy enabled="1"/> </runtime> </configuration> 

在app.config中将阻止您的辅助线程exceptionclosures应用程序。

这里是使用NLog完整示例

 using NLog; using System; using System.Windows; namespace MyApp { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { private static Logger logger = LogManager.GetCurrentClassLogger(); public App() { var currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledException += CurrentDomain_UnhandledException; } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { var ex = (Exception)e.ExceptionObject; logger.Error("UnhandledException caught : " + ex.Message); logger.Error("UnhandledException StackTrace : " + ex.StackTrace); logger.Fatal("Runtime terminating: {0}", e.IsTerminating); } } } 

使用NLog的示例代码,它将捕获AppDomain中所有线程, UI调度程序线程asynchronous函数抛出的exception:

App.xaml.cs:

 public partial class App : Application { private static Logger _logger = LogManager.GetCurrentClassLogger(); protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); SetupExceptionHandling(); } private void SetupExceptionHandling() { AppDomain.CurrentDomain.UnhandledException += (s, e) => LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException"); DispatcherUnhandledException += (s, e) => LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException"); TaskScheduler.UnobservedTaskException += (s, e) => LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException"); } private void LogUnhandledException(Exception exception, string source) { string message = $"Unhandled exception ({source})"; try { System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName(); message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version); } catch (Exception ex) { _logger.Error(ex, "Exception in LogUnhandledException"); } finally { _logger.Error(exception, message); } } 

像“VB的错误恢复下一个? 这听起来有点吓人。 首先build议是不要这样做。 第二个build议是不要这样做,不要考虑它。 你需要更好地隔离你的错误。 至于如何解决这个问题,这取决于你是如何构build代码的。 如果你正在使用像MVC或类似的模式,那么这应该不会太困难,绝对不会需要一个全局的exception吞服器。 其次,寻找一个像log4net的好日志库或使用跟踪。 我们需要知道更多的细节,比如你正在讨论什么样的exception,以及你的应用程序的哪些部分可能会导致抛出exception。