Вопрос по wpf, exception-handling, uncaughtexceptionhandler, c#, uncaught-exception – Следует использовать как AppDomain.UnhandledException, так и Application.DispatcherUnhandledException?

31

После прочтения нескольких отличных постов о разнице между AppDomain.UnhandledException и Application.DispatcherUnhandledException кажется, что я должен обработать оба. Это связано с тем, что с гораздо большей вероятностью пользователь может восстановиться после исключения, выдаваемого основным потоком пользовательского интерфейса (т. Е. Application.DispatcherUnhandledException). Правильный?

Кроме того, я должен также дать пользователю возможность продолжить программу для обоих или только Application.DispatcherUnhandledException?

Пример кода ниже обрабатывает как AppDomain.UnhandledException, так и Application.DispatcherUnhandledException, и оба дают пользователю возможность попытаться продолжить, несмотря на исключение.

[спасибо, и часть кода ниже взята из других ответов]

App.xaml

<code><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>
</code>

App.xaml.cs [отредактировано]

<code>/// <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;
}
</code>

Ваш Ответ

2   ответа
5

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 twice - подключив обработчик в xaml И в коде.

Я также не уверен, существует ли возможность передачи исключения для потока пользовательского интерфейса, но я подозреваю, что это может произойти. Вот почему они оба имеют e.handled = true в конце - это должно предотвратить его обработку дважды. Kyle
38
AppDomain.CurrentDomain.UnhandledException in theory catches all exceptions on all threads of the appdomain. I found this to be very unreliable, though.

Application.Current.DispatcherUnhandledException catches all exceptions on the UI thread. This seems to work reliably, and will replace the AppDomain.CurrentDomain.UnhandledException handler on the UI thread (takes priority). Use e.Handled = true to keep the application running.

For catching exceptions on other threads (in the best case, they are handled on their own thread), I found System.Threading.Tasks.Task (only .NET 4.0 and above) to be low-maintenance. Handle exceptions in tasks with the method .ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted). See my answer here for details.

@ Ханнобо: Правильно, скорректировано.
Здесь нетe.SetObserved() при обработкеAppDomain.CurrentDomain.UnhandledException, Вы путаете это с аргументами изTaskScheduler.UnobservedTaskException.

Похожие вопросы