207 votes

Gestionnaire d'exceptions globales .NET dans une application console

Question : Je veux définir un gestionnaire d'exception global pour les exceptions non gérées dans mon application console. En asp.net, on peut en définir un dans global.asax, et dans les applications / services Windows, on peut définir comme ci-dessous

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

Mais comment puis-je définir un gestionnaire d'exception global pour une application console ?
currentDomain semble ne pas fonctionner (.NET 2.0) ?

Edit:

Aïe, erreur stupide.
En VB.NET, il faut ajouter le mot clé "AddHandler" devant currentDomain, sinon on ne voit pas l'événement UnhandledException dans IntelliSense...
C'est parce que les compilateurs VB.NET et C# traitent différemment la gestion des événements.

291voto

Hans Passant Points 475940

Non, c'est la façon correcte de le faire. Cela a fonctionné exactement comme il se doit, quelque chose à partir de laquelle vous pouvez peut-être travailler:

utilisant System;

class Program {
    static vide Principe(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        jeter new Exception("Kaboom");
    }

    statique vide UnhandledExceptionTrapper(objet expéditeur, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Appuyez sur Entrée pour continuer");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

Gardez à l'esprit que vous ne pouvez pas attraper type et charger des exceptions de fichiers générées par le jitter de cette manière. Ils se produisent avant que votre méthode Main() ne commence à s'exécuter. Pour les attraper, il est nécessaire de retarder le jitter, déplacer le code risqué dans une autre méthode et appliquer l'attribut [MethodImpl(MethodImplOptions.NoInlining)] à celle-ci.

0 votes

Argh, erreur stupide, j'ai besoin d'ajouter AddHandler devant AppDomain.CurrentDomain pour voir "UnhandledException" en VB.NET...

3 votes

J'ai implémenté ce que vous avez proposé ici, mais je ne veux pas quitter l'application. Je veux juste le journaliser et continuer le processus (sans Console.ReadLine() ou tout autre perturbation du flux du programme. Mais ce que j'obtiens, c'est l'exception qui se relève encore et encore, et encore.

4 votes

@Shahrooz Jefri : Vous ne pouvez pas continuer une fois que vous avez une exception non gérée. La pile est corrompue et cela est terminal. Si vous avez un serveur, ce que vous pouvez faire dans UnhandledExceptionTrapper est de redémarrer le programme avec les mêmes arguments de ligne de commande.

23voto

BlueMonkMN Points 10838

Si vous avez une application à un seul thread, vous pouvez utiliser un simple bloc try/catch dans la fonction Main, cependant, cela ne couvre pas les exceptions qui peuvent être lancées en dehors de la fonction Main, sur d'autres threads, par exemple (comme indiqué dans d'autres commentaires). Ce code montre comment une exception peut provoquer la terminaison de l'application même si vous avez essayé de la gérer dans Main (remarquez comment le programme se termine correctement si vous appuyez sur Entrée et permettez à l'application de se terminer correctement avant que l'exception ne se produise, mais si vous la laissez s'exécuter, elle se termine assez malheureusement) :

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Attrapé une exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

Vous pouvez être notifié lorsque qu'un autre thread lance une exception pour effectuer un nettoyage avant la sortie de l'application, mais autant que je sache, vous ne pouvez pas, depuis une application console, forcer l'application à continuer de s'exécuter si vous ne gérez pas l'exception sur le thread à partir duquel elle est lancée sans utiliser certaines options de compatibilité obscures pour rendre l'application comporter comme elle l'aurait fait avec .NET 1.x. Ce code montre comment le thread principal peut être notifié des exceptions provenant d'autres threads, mais se terminera quand même malheureusement :

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Attrapé une exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notifié d'une exception de thread... l'application se termine.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

Alors, à mon avis, la manière la plus propre de le gérer dans une application console est de s'assurer que chaque thread a un gestionnaire d'exceptions au niveau racine :

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Attrapé une exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Attrapé une exception sur l'autre thread");
   }
}

0 votes

TRY CATCH ne fonctionne pas en mode release pour les erreurs inattendues :/

12voto

BlackICE Points 4561

Vous devez également gérer les exceptions provenant des threads :

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

Désolé, c'était pour winforms, pour tout thread que vous utilisez dans une application console, vous devrez l'encadrer dans un bloc try/catch. Les threads en arrière-plan qui rencontrent des exceptions non traitées ne provoquent pas la fin de l'application.

1voto

JustSomeDude Points 719

Je viens d'hériter d'une ancienne application console VB.NET et j'avais besoin de mettre en place un gestionnaire d'exceptions global. Comme cette question mentionne VB.NET plusieurs fois et est taguée avec VB.NET, mais que toutes les autres réponses ici sont en C#, j'ai pensé ajouter la syntaxe exacte pour une application VB.NET également.

Public Sub Main()
    REM Mettre en place un gestionnaire d'exceptions non gérées global.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Faire d'autres choses
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Journaliser l'exception ici et faire tout ce qui est nécessaire
End Sub

J'ai utilisé le marqueur de commentaire REM au lieu de l'apostrophe simple ici parce que Stack Overflow semblait mieux gérer la coloration syntaxique avec REM.

-13voto

Rodney Foley Points 3016

Ce que vous essayez de faire devrait fonctionner selon la documentation de MSDN pour .Net 2.0. Vous pourriez également essayer un bloc try/catch directement dans la méthode Main autour du point d'entrée de votre application console.

static void Main(string[] args)
{
    try
    {
        // Commencer le travail
    }
    catch (Exception ex)
    {
        // Afficher/Enregistrer l'exception
    }
    finally
    {
        // Nettoyer si nécessaire
    }
}

Et maintenant, votre bloc catch gérera tout ce qui n'a pas été attrapé (dans le thread principal). Il peut être gracieux et même redémarrer là où il en était si vous le souhaitez, ou vous pouvez simplement laisser l'application se terminer et enregistrer l'exception. Vous pouvez ajouter un bloc finally si vous souhaitez effectuer un nettoyage. Chaque thread nécessitera sa propre gestion des exceptions de haut niveau similaire à celle du thread principal.

Edité pour clarifier le point concernant les threads comme souligné par BlueMonkMN et montré en détail dans sa réponse.

1 votes

Les exceptions peuvent effectivement toujours être levées en dehors du bloc Main(), malheureusement. Ce n'est pas vraiment un "catch all" comme vous pourriez le penser. Voir la réponse de @Hans.

0 votes

@Mike Tout d'abord, j'ai dit que la façon dont il le faisait était correcte, et qu'il pourrait essayer un try / catch dans le main. Je ne sais pas pourquoi vous (ou quelqu'un d'autre) m'avez donné un vote négatif alors que j'étais d'accord avec Hans en fournissant simplement une autre réponse pour laquelle je ne m'attendais pas à recevoir une vérification. Ce n'est pas vraiment juste, et ensuite dire que l'alternative est incorrecte sans fournir de preuve sur la manière dont une exception qui peut être attrapée par le processus AppDomain UnhandledException qu'un try / catch dans Main ne peut pas attraper. Je trouve cela impoli de dire quelque chose est incorrect sans prouver pourquoi c'est incorrect, dire simplement que c'est ainsi, ne le rend pas ainsi.

0 votes

Il est assez évident que vous venez de voter négativement pour 4 de mes anciens messages de 2009 en représailles. Envisagez de modifier votre message et je retirerai mon vote négatif (et vous devriez annuler vos votes de colère que vous m'avez donnés, je ferai de même).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X