web-dev-qa-db-fra.com

Attraper globalement des exceptions dans une application WPF?

Nous avons une application WPF où certaines parties peuvent générer des exceptions lors de l'exécution. Je voudrais capturer globalement toute exception non gérée et les consigner, mais sinon, continuer l'exécution du programme comme si rien ne s'était passé (un peu comme VB's On Error Resume Next).

Est-ce possible en C #? Et si oui, où aurais-je besoin de mettre exactement le code de traitement des exceptions?

Actuellement, je ne vois pas de point où je pourrais enrouler un try/catch et capturer toutes les exceptions qui pourraient se produire. Et même alors, j'aurais laissé tout ce qui a été exécuté à cause de la prise. Ou est-ce que je pense dans des directions horriblement mauvaises ici?

ETA: Parce que beaucoup de personnes ci-dessous l'ont signalé: l'application ne vise pas à contrôler les centrales nucléaires. Si cela se bloque, ce n'est pas si grave, mais les exceptions aléatoires, qui sont principalement liées à l'assurance-chômage, sont une nuisance dans le contexte où elles seraient utilisées. Il y en avait (et il y a probablement encore) quelques-uns d'entre eux et puisqu'il utilise une architecture de plug-in et peut être étendu par d'autres (également les étudiants dans ce cas; donc no développeurs expérimentés capables d’écrire du code totalement exempt d’erreur).

En ce qui concerne les exceptions interceptées: je les enregistre dans un fichier journal, y compris la trace complète de la pile. C'était le but de cet exercice. Juste pour contrer ces gens qui prenaient mon analogie avec l'OERN de VB trop littéralement.

Je sais qu'ignorer aveuglément certaines classes d'erreurs est dangereux et risque de corrompre mon instance d'application. Comme indiqué précédemment, ce programme n’est critique pour personne. Personne dans son esprit ne parierait la survie de la civilisation humaine. C'est simplement un petit outil pour tester certaines approches de conception par rapport à. génie logiciel.

Pour l'utilisation immédiate de l'application, peu de choses peuvent se produire avec une exception:

  • Pas de gestion des exceptions - boîte de dialogue d'erreur et sortie de l'application. L'expérience doit être répétée, bien que probable avec un autre sujet. Aucune erreur n'a été enregistrée, ce qui est regrettable.
  • Gestion des exceptions génériques - erreur bénigne piégée, aucun dommage causé. Cela devrait être le cas commun jugé à partir de toutes les erreurs que nous avons constatées au cours du développement. Ignorer ce type d'erreur ne devrait avoir aucune conséquence immédiate; les structures de données de base sont suffisamment bien testées pour y survivre facilement.
  • Gestion des exceptions génériques - erreur grave piégée, peut-être planter plus tard. Cela peut arriver rarement. Nous ne l'avons jamais vu jusqu'à présent. L'erreur est quand même enregistrée et un crash peut être inévitable. Donc, c'est conceptuellement similaire au tout premier cas. Sauf que nous avons une trace de pile. Et dans la majorité des cas, l'utilisateur ne le remarquera même pas.

En ce qui concerne les données d’expérience générées par le programme: une erreur grave entraînerait, au pire, aucune donnée à enregistrer. Des changements subtils qui modifient légèrement le résultat de l'expérience sont plutôt improbables. Et même dans ce cas, si les résultats semblaient douteux, l'erreur était enregistrée; on peut toujours jeter ce point de données s'il s'agit d'une valeur aberrante totale.

Pour résumer: Oui, je me considère toujours au moins partiellement sain d'esprit et je ne considère pas qu'une routine globale de traitement des exceptions qui laisse le programme en cours d'exécution soit nécessairement totalement perverse. Comme cela a été dit deux fois auparavant, une telle décision pourrait être valide, en fonction de la demande. Dans ce cas, il a été jugé une décision valide et non totale et totale. Pour toute autre application, cette décision pourrait sembler différente. Mais s'il vous plaît, ne m'accusez pas, pas plus que les autres personnes ayant travaillé sur ce projet, à potentiellement faire sauter le monde simplement parce que nous ignorons les erreurs.

Note latérale: Il y a exactement un utilisateur pour cette application. Ce n'est pas quelque chose comme Windows ou Office qui est utilisé par des millions de personnes et où le coût des exceptions déroge pour l'utilisateur serait déjà très différent.

221
Joey

Utilisez le Application.DispatcherUnhandledException Event . Voir cette question pour un résumé (voir réponse de Drew Noakes ).

Sachez qu'il y aura toujours des exceptions qui empêcheront la reprise de votre application, par exemple après un débordement de pile, une mémoire épuisée ou une perte de connectivité réseau pendant que vous essayez de sauvegarder dans la base de données.

173
David Schmitt

Exemple de code utilisant NLog qui interceptera les exceptions émises par tous les threads de l’AppDomain, à partir du thread de répartiteur d’UI et à partir du asynchrone fonctions:

App.xaml.cs:

public partial class App : Application
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        SetupExceptionHandling();
    }

    private void SetupExceptionHandling()
    {
        AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");

        DispatcherUnhandledException += (s, e) =>
            LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");

        TaskScheduler.UnobservedTaskException += (s, e) =>
            LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
    }

    private void LogUnhandledException(Exception exception, string source)
    {
        string message = $"Unhandled exception ({source})";
        try
        {
            System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
            message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Exception in LogUnhandledException");
        }
        finally
        {
            _logger.Error(exception, message);
        }
    }
34
Hüseyin Yağlı

AppDomain.UnhandledException Evénement

Cet événement fournit une notification des exceptions non capturées. Il permet à l'application de consigner des informations sur l'exception avant que le gestionnaire de système par défaut ne le signale à l'utilisateur et termine l'application.

   public App()
   {
      AppDomain currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);    
   }

   static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }

Si l'événement UnhandledException est géré dans le domaine d'application par défaut, il y est déclenché pour toute exception non gérée dans un thread, quel que soit le domaine d'application dans lequel le thread a démarré. Si le thread a démarré dans un domaine d'application doté d'un gestionnaire d'événements pour UnhandledException, l'événement est déclenché dans ce domaine d'application. Si ce domaine d'application n'est pas le domaine d'application par défaut et s'il existe également un gestionnaire d'événements dans le domaine d'application par défaut, l'événement est déclenché dans les deux domaines d'application.

Par exemple, supposons qu'un thread commence dans le domaine d'application "AD1", appelle une méthode dans le domaine d'application "AD2" et à partir de là, appelle une méthode dans le domaine d'application "AD3", où il lève une exception. Le premier domaine d'application dans lequel l'événement UnhandledException peut être déclenché est "AD1". Si ce domaine d'application n'est pas le domaine d'application par défaut, l'événement peut également être déclenché dans le domaine d'application par défaut.

28
CharithJ

En plus de ce que d’autres ont mentionné ici, notez que la combinaison du Application.DispatcherUnhandledException _ (et ses similaires ) avec

<configuration>
  <runtime>  
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

dans le app.config empêchera votre exception de threads secondaires d’arrêter l’application.

17
Hertzel Guinness

Voici un exemple complet en utilisant NLog

using NLog;
using System;
using System.Windows;

namespace MyApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public App()
        {
            var currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception)e.ExceptionObject;
            logger.Error("UnhandledException caught : " + ex.Message);
            logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
            logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
        }        
    }


}
1
Developer