首页 文章

全局捕获WPF应用程序中的异常?

提问于
浏览
207

我们有一个WPF应用程序,其中部分可能会在运行时抛出异常 . 我'd like to globally catch any unhandled exception and log them, but otherwise continue program execution as if nothing happened (kinda like VB' s On Error Resume Next ) .

这可能在C#中吗?如果是这样,我究竟需要在哪里放置异常处理代码?

目前我看不到任何单一点,我可以包装 try / catch ,哪些可以捕获可能发生的所有异常 . 即便如此,由于捕获,我会留下任何被执行的东西 . 或者我在这里想到可怕的错误方向?

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

至于被捕获的异常:我将它们记录到日志文件中,包括完整的堆栈跟踪 . 这就是这项工作的重点 . 只是为了对抗那些在字面上对我的类比较为VB的OERN的人 .

我知道盲目地忽略某些类错误是危险的,可能会破坏我的应用程序实例 . 如前所述,这个程序对任何人来说都不是关键任务 . 在他们正确的思想中没有人会打赌人类文明的存在 . 它只是一个用于测试某些设计方法的小工具 . 软件工程 .

为了立即使用该应用程序,异常可能会发生很多事情:

  • 无异常处理 - 错误对话框和应用程序退出 . 必须重复实验,尽管可能与另一个主题有关 . 没有记录任何错误,这是不幸的 .

  • 通用异常处理 - 良性错误被困,没有造成伤害 . 这应该是我们在开发过程中看到的所有错误判断的常见情况 . 忽视这种错误不应该立即产生后果;核心数据结构经过充分测试,可以轻松应对 .

  • 通用异常处理 - 严重错误被捕获,可能在以后崩溃 . 这可能很少发生 . 我们甚至没有注意到 .

至于程序产生的实验数据:严重错误最坏的情况是不会导致数据被记录 . 稍微改变实验结果的细微变化是不太可能的 . 即使在这种情况下,如果结果看起来很可疑,则会记录错误;如果它是一个完整的异常值,那么仍然可以丢弃该数据点 .

总结一下:是的,我认为自己仍然至少部分理智,我不认为一个全局异常处理例程让程序运行必然是完全邪恶的 . 如前所述,根据申请,这样的决定可能是有效的 . 在这种情况下,它被认为是一个有效的决定,而不是完全和彻底的废话 . For any other application that decision might look different. 但请不要忽视错误 .

附注:该应用程序只有一个用户 . 数百万人使用的不是像Windows或Office这样的东西,其中向用户提供异常泡沫的成本本来就已经非常不同了 .

6 回答

  • 164

    AppDomain.UnhandledException活动

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

    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事件,则会在任何线程中针对任何未处理的异常引发它,无论线程在哪个应用程序域中启动 . 如果线程在具有UnhandledException的事件处理程序的应用程序域中启动,该事件在该应用程序域中引发 . 如果该应用程序域不是默认应用程序域,并且默认应用程序域中也存在事件处理程序,则会在两个应用程序域中引发该事件 . 例如,假设一个线程在应用程序域“AD1”中启动,在应用程序域“AD2”中调用一个方法,并从那里调用应用程序域“AD3”中的方法,它会抛出异常 . 可以引发UnhandledException事件的第一个应用程序域是“AD1” . 如果该应用程序域不是默认应用程序域,则还可以在默认应用程序域中引发该事件 .

  • 17

    使用 NLog 的示例代码将捕获从 all threads in the AppDomainUI dispatcher threadasync functions 抛出的异常:

    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);
            }
        }
    
  • 1

    这是使用 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);
            }        
        }
    
    
    }
    
  • -3

    除了其他人在这里提到的,请注意将 Application.DispatcherUnhandledException (和它的similars)结合起来

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

    app.config 中将阻止您的辅助线程异常从关闭应用程序 .

  • 17

    比如“VB的On Error Resume Next?”这听起来有点可怕 . 第一个建议是不要这样做 . 第二个建议是不要这样做,不要考虑它 . 您需要更好地隔离故障 . 至于如何解决这个问题,这取决于你的代码是如何构建的 . 如果你使用像MVC之类的模式,那么这应该不会太困难,并且绝对不需要全局异常吞咽 . 其次,寻找像log4net这样的好的日志库或使用跟踪 . 我们需要了解更多详细信息,例如您正在讨论的异常类型以及应用程序的哪些部分可能会导致异常被抛出 .

  • 27

    使用Application.DispatcherUnhandledException Event . 有关摘要,请参阅this question(请参阅Drew Noakes' answer) .

    请注意,在您尝试保存到数据库时,仍会存在阻止成功恢复应用程序的异常,例如堆栈溢出,内存耗尽或网络连接丢失之后 .

相关问题