Должен использовать как AppDomain.UnhandledException, так и Application.DispatcherUnhandledException?
После прочтения некоторых замечательных сообщений о различии между AppDomain.UnhandledException и Application.DispatcherUnhandledException, похоже, что я должен обрабатывать оба. Это связано с тем, что значительно более вероятно, что пользователь сможет восстановить из исключения, вызванного основным потоком пользовательского интерфейса (т.е. 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 [redacted]
/// <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;
}
Ответы
Ответ 1
-
AppDomain.CurrentDomain.UnhandledException
теоретически ловит все исключения во всех потоках appdomain. Вместо e.Handled
используйте e.SetObserved()
, если вы не хотите, чтобы процесс завершился. Я обнаружил, что это очень ненадежно.
-
Application.Current.DispatcherUnhandledException
захватывает все исключения в потоке пользовательского интерфейса. Это, похоже, работает надежно и заменяет обработчик AppDomain.CurrentDomain.UnhandledException
на потоке пользовательского интерфейса (имеет приоритет). Используйте e.Handled = true
, чтобы приложение работало.
-
Для обнаружения исключений в других потоках (в лучшем случае они обрабатываются в их собственном потоке), я обнаружил, что System.Threading.Tasks.Task(только .NET 4.0 и выше) является низким уровнем обслуживания. Обработка исключений в задачах с помощью метода .ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted)
. Подробнее см. Мой ответ здесь.
Ответ 2
Обработчик 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 И в коде.