【问题标题】:Should use both AppDomain.UnhandledException and Application.DispatcherUnhandledException?应该同时使用 AppDomain.UnhandledException 和 Application.DispatcherUnhandledException 吗?
【发布时间】:2012-04-21 02:56:12
【问题描述】:

在阅读了一些关于 AppDomain.UnhandledException 和 Application.DispatcherUnhandledException 之间区别的优秀文章之后,看来我应该同时处理这两个问题。这是因为用户更有可能从主 UI 线程(即 Application.DispatcherUnhandledException)抛出的异常中恢复。对吗?

另外,我是否也应该让用户有机会为两者继续程序,还是只为 Application.DispatcherUnhandledException 提供程序?

下面的示例代码同时处理 AppDomain.UnhandledException 和 Application.DispatcherUnhandledException,并且都为用户提供了在出现异常时尝试继续的选项。

[谢谢,下面的一些代码是从其他答案中提取的]

App.xaml

<Application x:Class="MyProgram.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Startup="App_StartupUriEventHandler"
         Exit="App_ExitEventHandler"
         DispatcherUnhandledException="AppUI_DispatcherUnhandledException">
    <Application.Resources>
    </Application.Resources>
</Application>

App.xaml.cs [已编辑]

/// <summary>
/// Add dispatcher for Appdomain.UnhandledException
/// </summary>
public App()
    : base()
{
    this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}

/// <summary>
/// Catch unhandled exceptions thrown on the main UI thread and allow 
/// option for user to continue program. 
/// The OnDispatcherUnhandledException method below for AppDomain.UnhandledException will handle all other exceptions thrown by any thread.
/// </summary>
void AppUI_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    if (e.Exception == null)
    {
        Application.Current.Shutdown();
        return;
    }
    string errorMessage = string.Format("An application error occurred. If this error occurs again there seems to be a serious bug in the application, and you better close it.\n\nError:{0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)", e.Exception.Message);
    //insert code to log exception here
    if (MessageBox.Show(errorMessage, "Application User Interface Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)
    {
        if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
        {
            Application.Current.Shutdown();
        }
    }
    e.Handled = true;
}

/// <summary>
/// Catch unhandled exceptions not thrown by the main UI thread.
/// The above AppUI_DispatcherUnhandledException method for DispatcherUnhandledException will only handle exceptions thrown by the main UI thread. 
/// Unhandled exceptions caught by this method typically terminate the runtime.
/// </summary>
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    string errorMessage = string.Format("An application error occurred. If this error occurs again there seems to be a serious bug in the application, and you better close it.\n\nError:{0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)", e.Exception.Message);
    //insert code to log exception here
    if (MessageBox.Show(errorMessage, "Application UnhandledException Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)
    {
        if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
        {
            Application.Current.Shutdown();
        }
    }
    e.Handled = true;
}

【问题讨论】:

    标签: c# wpf exception-handling uncaught-exception uncaughtexceptionhandler


    【解决方案1】:

    AppDomain.UnhandledException 处理程序连接为:

    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    

    但我找不到在处理程序中标记为已处理的方法 - 所以无论你做什么,这似乎总是导致应用程序关闭。所以我不认为这很有用。

    最好处理Application.Current.DispatcherUnhandledException 并测试CommunicationObjectFaultedException - 因为您可以通过重新初始化代理来从中恢复 - 就像您在初始连接时所做的那样。例如:

    void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
        if (e.Exception is CommunicationObjectFaultedException) { //|| e.Exception is InvalidOperationException) {
            Reconnect();
            e.Handled = true;
        }
        else {
            MessageBox.Show(string.Format("An unexpected error has occured:\n{0}.\nThe application will close.", e.Exception));
            Application.Current.Shutdown();
        }
    }
    
    public bool Reconnect() {
        bool ok = false;
        MessageBoxResult result = MessageBox.Show("The connection to the server has been lost.  Try to reconnect?", "Connection lost", MessageBoxButton.YesNo);
        if (result == MessageBoxResult.Yes)
            ok = Initialize();
        if (!ok)
            Application.Current.Shutdown();
    }
    

    Initialize 包含您的初始代理实例化/连接代码。

    在您上面发布的代码中,我怀疑您正在处理DispatcherUnhandledException 两次 - 通过在 xaml 和代码中连接处理程序。

    【讨论】:

    • 我也不确定是否有可能同时为 UI 线程处理异常,但我怀疑这可能会发生。这就是为什么他们最后都有 e.handled = true - 这应该可以防止它被处理两次。
    【解决方案2】:
    • AppDomain.CurrentDomain.UnhandledException 理论上可以捕获 appdomain 的所有线程上的所有异常。不过,我发现这非常不可靠。
    • Application.Current.DispatcherUnhandledException 捕获 UI 线程上的所有异常。这似乎工作可靠,并将替换 UI 线程上的 AppDomain.CurrentDomain.UnhandledException 处理程序(优先)。使用e.Handled = true 保持应用程序运行。

    • 为了在其他线程上捕获异常(在最好的情况下,它们在自己的线程上处理),我发现 System.Threading.Tasks.Task(仅限 .NET 4.0 及更高版本)维护成本低。使用方法.ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted) 处理任务中的异常。详情见我的回答here

    【讨论】:

    • 您能否详细说明 .UnhandledException 不可靠?
    • @Brondahl 它似乎有时可以工作(进入异常处理程序),而在其他时候则不行。我最近没有检查是否仍然是这种情况。
    猜你喜欢
    • 1970-01-01
    • 2014-08-12
    • 2021-05-18
    • 2021-03-27
    • 2016-01-24
    • 2011-07-04
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    相关资源
    最近更新 更多