WPF gestionnaire global d'exception [dupliquer]

cette question a déjà une réponse ici:

parfois, dans des circonstances non reproductibles, mon application WPF s'effondre sans aucun message. L'application se ferme instantanément.

Où est le meilleur endroit pour mettre en œuvre le bloc global Try/Catch. Au moins je dois mettre en place un messagebox avec: "désolé pour le dérangement ..."

288
demandé sur Jakob Möllås 2009-09-24 19:40:00

9 réponses

vous pouvez gérer le AppDomain.UnhandledException événement

EDIT: en fait, cet événement est probablement plus adéquat: Application.DispatcherUnhandledException

145
répondu Thomas Levesque 2009-09-24 15:50:17

, Vous pouvez intercepter les exceptions non gérées à différents niveaux:

  1. AppDomain.CurrentDomain.UnhandledException de tous les fils dans L'AppDomain.
  2. Dispatcher.UnhandledException à partir d'un seul fil de répartiteur UI spécifique.
  3. Application.Current.DispatcherUnhandledException du fil main UI dispatcher dans votre application WPF.
  4. TaskScheduler.UnobservedTaskException de chaque Appdomaine qui utilise un planificateur de tâches pour des opérations asynchrones.

vous devriez considérer à quel niveau vous avez besoin pour piéger les exceptions sans entrave.

décider entre #2 et #3 dépend de si vous utilisez plus d'un fil WPF. C'est une situation assez exotique et si vous n'êtes pas sûr de savoir si vous l'êtes ou non, alors il est très probable que vous ne l'êtes pas.

453
répondu Drew Noakes 2016-11-17 17:07:59

un exemple rapide de code pour L'Application.Répartiteur.Sauf exception:

public App() {
    this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}

void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
    string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
    MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    e.Handled = true;
}

j'ai ajouté ce code dans L'annexe.XAML.cs

101
répondu Sergey 2018-09-22 05:53:51

j'utilise le code suivant dans Mes applications WPF pour afficher une boîte de dialogue" Désolé for the inconvenience " chaque fois qu'une exception se produit. Il affiche le message d'exception, et demande à l'utilisateur s'ils veulent fermer l'application ou ignorer l'exception et continuer (ce dernier cas est pratique en cas de non-fatale des exceptions se produisent et l'utilisateur peut toujours normalement continuer à utiliser l'application).

Dans L'App.XAML ajoute le gestionnaire D'événements de démarrage:

<Application .... Startup="Application_Startup">

In App.XAML.cs de code à ajouter de Démarrage fonction de gestionnaire d'événement qui va enregistrer l'application gestionnaire d'événement:

using System.Windows.Threading;

private void Application_Startup(object sender, StartupEventArgs e)
{
    // Global exception handling  
    Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException);    
}

void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{    
    \#if DEBUG   // In debug mode do not custom-handle the exception, let Visual Studio handle it

    e.Handled = false;

    \#else

    ShowUnhandledException(e);    

    \#endif     
}

void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e)
{
    e.Handled = true;

    string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction 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 + (e.Exception.InnerException != null ? "\n" + 
    e.Exception.InnerException.Message : null));

    if (MessageBox.Show(errorMessage, "Application 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();
    } 
}
40
répondu jurev 2016-06-09 06:33:10

la meilleure réponse est probablement https://stackoverflow.com/a/1472562/601990 .

Voici un code qui montre comment l'utiliser:

App.XAML.cs

public sealed partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // setting up the Dependency Injection container
        var resolver = ResolverFactory.Get();

        // getting the ILogger or ILog interface
        var logger = resolver.Resolve<ILogger>();
        RegisterGlobalExceptionHandling(logger);

        // Bootstrapping Dependency Injection 
        // injects ViewModel into MainWindow.xaml
        // remember to remove the StartupUri attribute in App.xaml
        var mainWindow = resolver.Resolve<Pages.MainWindow>();
        mainWindow.Show();
    }

    private void RegisterGlobalExceptionHandling(ILogger log)
    {
        // this is the line you really want 
        AppDomain.CurrentDomain.UnhandledException += 
            (sender, args) => CurrentDomainOnUnhandledException(args, log);

        // optional: hooking up some more handlers
        // remember that you need to hook up additional handlers when 
        // logging from other dispatchers, shedulers, or applications

        Application.Dispatcher.UnhandledException += 
            (sender, args) => DispatcherOnUnhandledException(args, log);

        Application.Current.DispatcherUnhandledException +=
            (sender, args) => CurrentOnDispatcherUnhandledException(args, log);

        TaskScheduler.UnobservedTaskException += 
            (sender, args) => TaskSchedulerOnUnobservedTaskException(args, log);
    }

    private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        args.SetObserved();
    }

    private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        // args.Handled = true;
    }

    private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        // args.Handled = true;
    }

    private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log)
    {
        var exception = args.ExceptionObject as Exception;
        var terminatingMessage = args.IsTerminating ? " The application is terminating." : string.Empty;
        var exceptionMessage = exception?.Message ?? "An unmanaged exception occured.";
        var message = string.Concat(exceptionMessage, terminatingMessage);
        log.Error(exception, message);
    }
}
12
répondu MovGP0 2017-05-23 11:33:26

en plus des posts ci-dessus:

Application.Current.DispatcherUnhandledException

n'attrapera pas les exceptions qui sont lancées d'un autre thread que le thread principal. Vous devez gérer ces exceptions sur son fil conducteur. Mais si vous voulez les manipuler sur votre gestionnaire d'exception global, vous pouvez le passer au fil principal:

 System.Threading.Thread t = new System.Threading.Thread(() =>
    {
        try
        {
            ...
            //this exception will not be catched by 
            //Application.DispatcherUnhandledException
            throw new Exception("huh..");
            ...
        }
        catch (Exception ex)
        {
            //But we can handle it in the throwing thread
            //and pass it to the main thread wehre Application.
            //DispatcherUnhandledException can handle it
            System.Windows.Application.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Normal,
                new Action<Exception>((exc) =>
                    {
                      throw new Exception("Exception from another Thread", exc);
                    }), ex);
        }
    });
11
répondu Tobias Hoefer 2013-02-18 16:03:03

pour compléter la réponse de Thomas, la classe Application a aussi l'événement DispatcherUnhandledException que vous pouvez gérer.

3
répondu dustyburwell 2009-09-24 15:52:55

une solution complète est ici

il est expliqué très agréable avec le code échantillon. Cependant, attention à ne pas fermer l'application.Ajouter la ligne Application.Actuel.Arrêt(); normalement fermer l'application.

3
répondu karpanai 2014-12-08 10:53:28

comme indiqué ci-dessus

de l'Application.Actuel.DispatcherUnhandledException ne serez pas attraper exceptions qui sont lancées à partir d'un autre thread puis le thread principal.

cela dépend de la façon dont le fil a été créé

un cas qui n'est pas traité par Application.Actuel.DispatcherUnhandledException est un système.Windows.Forme.Minuterie pour quelle Application.ThreadException peut être utilisé pour poignée de ces si vous exécutez Formes sur d'autres threads que le thread principal, vous devrez configurer l'Application.ThreadException de chaque thread

1
répondu Jens 2014-09-11 11:16:46