web-dev-qa-db-ja.com

AppDomain.UnhandledExceptionとApplication.DispatcherUnhandledExceptionの両方を使用する必要がありますか?

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;
}
34
Kyle
  • _AppDomain.CurrentDomain.UnhandledException_は、理論的にはアプリドメインのすべてのスレッドですべての例外をキャッチします。しかし、これは非常に信頼できないことがわかりました。
  • _Application.Current.DispatcherUnhandledException_は、UIスレッドのすべての例外をキャッチします。これは確実に機能するようで、UIスレッドの_AppDomain.CurrentDomain.UnhandledException_ハンドラーを置き換えます(優先されます)。 _e.Handled = true_を使用して、アプリケーションを実行し続けます。

  • 他のスレッドで例外をキャッチするために(最良の場合、それらは独自のスレッドで処理されます)、System.Threading.Tasks.Task(.NET 4.0以降のみ)はメンテナンスが少ないことがわかりました。メソッド.ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted)を使用してタスクの例外を処理します。詳細については、私の回答 こちら を参照してください。

39
Mike Fuchs

AppDomain.UnhandledExceptionハンドラーは次のように配線されています:

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

しかし、ハンドラーで処理されたものとしてフラグを立てる方法を見つけることができませんでした。そのため、何をしてもアプリのシャットダウンが常に発生するようです。だから、これは多くの用途ではないと思います。

処理するのが良いApplication.Current.DispatcherUnhandledExceptionCommunicationObjectFaultedExceptionをテストするには-プロキシを再初期化するだけで回復できるため、初期接続時とまったく同じです。例えば:

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には、初期プロキシのインスタンス化/接続コードがあります。

上記で投稿したコードでは、DispatcherUnhandledExceptiontwice-ハンドラーをxamlとコードで配線することによって処理していると思います。

5
Ricibob