web-dev-qa-db-fra.com

Doit utiliser à la fois AppDomain.UnhandledException et Application.DispatcherUnhandledException?

Après avoir lu d'excellents articles sur la différence entre AppDomain.UnhandledException et Application.DispatcherUnhandledException, il semble que je devrais gérer les deux. En effet, il est beaucoup plus probable que l'utilisateur puisse récupérer d'une exception levée par le thread d'interface utilisateur principal (c'est-à-dire Application.DispatcherUnhandledException). Correct?

En outre, dois-je également donner à l'utilisateur une chance de continuer le programme pour les deux, ou simplement pour Application.DispatcherUnhandledException?

L'exemple de code ci-dessous gère à la fois AppDomain.UnhandledException et Application.DispatcherUnhandledException, et les deux donnent à l'utilisateur la possibilité d'essayer de continuer malgré l'exception.

[merci et une partie du code ci-dessous est extraite d'autres réponses]

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 [expurgé]

/// <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 En théorie intercepte toutes les exceptions sur tous les threads du domaine d'application. J'ai trouvé cela très peu fiable, cependant.
  • Application.Current.DispatcherUnhandledException Intercepte toutes les exceptions sur le thread d'interface utilisateur. Cela semble fonctionner de manière fiable et remplacera le gestionnaire AppDomain.CurrentDomain.UnhandledException Sur le thread d'interface utilisateur (a priorité). Utilisez e.Handled = true Pour maintenir l'application en cours d'exécution.

  • Pour intercepter les exceptions sur d'autres threads (dans le meilleur des cas, elles sont gérées sur leur propre thread), j'ai trouvé que System.Threading.Tasks.Task (uniquement .NET 4.0 et supérieur) nécessitait peu de maintenance. Gérez les exceptions dans les tâches avec la méthode .ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted). Voir ma réponse ici pour plus de détails.

39
Mike Fuchs

Une AppDomain.UnhandledException le gestionnaire est câblé en tant que:

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

Mais je ne pouvais pas trouver un moyen de marquer comme géré dans le gestionnaire - donc cela semble toujours entraîner la fermeture d'une application, quoi que vous fassiez. Je ne pense donc pas que ce soit très utile.

Mieux vaut gérer Application.Current.DispatcherUnhandledException et pour tester CommunicationObjectFaultedException - comme vous pouvez le récupérer en réinitialisant simplement votre proxy - exactement comme vous l'avez fait lors de la connexion initiale. Par exemple:

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();
}

où Initialize a votre code d'instanciation/connexion proxy initial.

Dans le code que vous avez posté ci-dessus, je soupçonne que vous gérez DispatcherUnhandledExceptiondeux fois - en câblant un gestionnaire en xaml ET en code.

5
Ricibob