关于c#:WPF全局异常处理程序

WPF global exception handler

本问题已经有最佳答案,请猛点这里访问。

有时,在不可复制的情况下,我的WPF应用程序在没有任何消息的情况下崩溃。应用程序只需立即关闭。

哪里是实现全局Try/Catch块的最佳位置。至少我必须实现一个消息框:"抱歉给您带来不便…"


您可以在不同的级别捕获未处理的异常:

  • 来自AppDomain中所有线程的AppDomain.CurrentDomain.UnhandledException
  • 来自单个特定UI调度程序线程的Dispatcher.UnhandledException
  • 来自WPF应用程序中主UI调度程序线程的Application.Current.DispatcherUnhandledException
  • 每个AppDomain中使用任务调度程序进行异步操作的TaskScheduler.UnobservedTaskException
  • 您应该考虑在什么级别捕获未处理的异常。

    在2和3之间做出决定取决于您是否使用了多个WPF线程。这是一种异国情调,如果你不确定自己是否是,那么很可能你不是。


    您可以处理AppDomain.UnhandledException事件

    编辑:实际上,这个事件可能更合适:Application.DispatcherUnhandledException


    application.dispatcher.unhandledException的代码示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public App() {
        this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
    }

    void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
        string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
        MessageBox.Show(errorMessage,"Error", MessageBoxButton.OK, MessageBoxImage.Error);
        // OR whatever you want like logging etc. MessageBox it's just example
        // for quick debugging etc.
        e.Handled = true;
    }

    我在app.xaml.cs中添加了此代码


    每当发生未处理的异常时,我在WPF应用程序中使用以下代码来显示"抱歉给您带来不便"对话框。它显示异常消息,询问用户是否要关闭应用程序或忽略异常并继续(后一种情况在发生非致命异常且用户仍可以正常继续使用应用程序时很方便)。

    在app.xaml中添加启动事件处理程序:

    1
    <Application .... Startup="Application_Startup">

    在app.xaml.cs代码中,添加将注册全局应用程序事件处理程序的启动事件处理程序函数:

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    using System.Windows.Threading;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        // Global exception handling  
        Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException);    
    }

    void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {    
        \#if DEBUG   // In debug mode do not custom-handle the exception, let Visual Studio handle it

        e.Handled = false;

        \#else

        ShowUnhandledException(e);    

        \#endif    
    }

    void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e)
    {
        e.Handled = true;

        string errorMessage = string.Format("An application error occurred.
    Please check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction in the application, and you better close it.

    Error: {0}

    Do you want to continue?
    (if you click Yes you will continue with your work, if you click No the application will close)"
    ,

        e.Exception.Message + (e.Exception.InnerException != null ?"
    "
    +
        e.Exception.InnerException.Message : null));

        if (MessageBox.Show(errorMessage,"Application Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)   {
            if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!
    Do you really want to close it?"
    ,"Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
        {
            Application.Current.Shutdown();
        }
    }


    最好的答案可能是https://stackoverflow.com/a/1472562/601990。

    下面是一些演示如何使用它的代码:

    代码文件

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    public sealed partial class App
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            // setting up the Dependency Injection container
            var resolver = ResolverFactory.Get();

            // getting the ILogger or ILog interface
            var logger = resolver.Resolve<ILogger>();
            RegisterGlobalExceptionHandling(logger);

            // Bootstrapping Dependency Injection
            // injects ViewModel into MainWindow.xaml
            // remember to remove the StartupUri attribute in App.xaml
            var mainWindow = resolver.Resolve<Pages.MainWindow>();
            mainWindow.Show();
        }

        private void RegisterGlobalExceptionHandling(ILogger log)
        {
            // this is the line you really want
            AppDomain.CurrentDomain.UnhandledException +=
                (sender, args) => CurrentDomainOnUnhandledException(args, log);

            // optional: hooking up some more handlers
            // remember that you need to hook up additional handlers when
            // logging from other dispatchers, shedulers, or applications

            Application.Dispatcher.UnhandledException +=
                (sender, args) => DispatcherOnUnhandledException(args, log);

            Application.Current.DispatcherUnhandledException +=
                (sender, args) => CurrentOnDispatcherUnhandledException(args, log);

            TaskScheduler.UnobservedTaskException +=
                (sender, args) => TaskSchedulerOnUnobservedTaskException(args, log);
        }

        private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log)
        {
            log.Error(args.Exception, args.Exception.Message);
            args.SetObserved();
        }

        private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
        {
            log.Error(args.Exception, args.Exception.Message);
            // args.Handled = true;
        }

        private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
        {
            log.Error(args.Exception, args.Exception.Message);
            // args.Handled = true;
        }

        private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log)
        {
            var exception = args.ExceptionObject as Exception;
            var terminatingMessage = args.IsTerminating ?" The application is terminating." : string.Empty;
            var exceptionMessage = exception?.Message ??"An unmanaged exception occured.";
            var message = string.Concat(exceptionMessage, terminatingMessage);
            log.Error(exception, message);
        }
    }


    除上述职位外:

    1
    Application.Current.DispatcherUnhandledException

    不会捕获从另一个线程然后从主线程抛出的异常。您必须在实际线程上处理这些异常。但是,如果要在全局异常处理程序上处理它们,可以将其传递给主线程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     System.Threading.Thread t = new System.Threading.Thread(() =>
        {
            try
            {
                ...
                //this exception will not be catched by
                //Application.DispatcherUnhandledException
                throw new Exception("huh..");
                ...
            }
            catch (Exception ex)
            {
                //But we can handle it in the throwing thread
                //and pass it to the main thread wehre Application.
                //DispatcherUnhandledException can handle it
                System.Windows.Application.Current.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new Action<Exception>((exc) =>
                        {
                          throw new Exception("Exception from another Thread", exc);
                        }), ex);
            }
        });


    这里有一个完整的解决方案

    用示例代码解释得很好。但是,注意不要关闭应用程序。添加行application.current.shutdown();优雅地关闭应用程序。


    为了补充托马斯的答案,Application类也有可以处理的DispatcherUnhandledException事件。


    如上所述

    Application.Current.DispatcherUnhandledException will not catch
    exceptions that are thrown from another thread then the main thread.

    实际情况取决于线程是如何创建的

    application.current.dispatcherUnhandledException无法处理的一种情况是System.Windows.Forms.Timer,可以使用application.threadeException来处理这些异常。如果在主线程以外的其他线程上运行表单,则需要从每个此类线程设置application.threadeexception