223 Stimmen

.NET Global Exception Handler in Konsolenanwendung

Frage: Ich möchte einen globalen Exception-Handler für unbehandelte Exceptions in meiner Konsolenanwendung definieren. In asp.net kann man einen in global.asax definieren, und in Windows-Anwendungen /services kann man wie folgt definieren

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

Aber wie kann ich einen globalen Exception-Handler für eine Konsolenanwendung definieren?
currentDomain scheint nicht zu funktionieren (.NET 2.0) ?

Bearbeiten:

Argh, dummer Fehler.
In VB.NET muss man das "AddHandler"-Schlüsselwort vor currentDomain hinzufügen, sonst sieht man das UnhandledException-Ereignis nicht in IntelliSense...
Das liegt daran, dass die VB.NET- und C#-Compiler die Ereignisbehandlung unterschiedlich behandeln.

318voto

Hans Passant Punkte 894572

Nein, das ist die richtige Vorgehensweise. Das hat genau so funktioniert, wie es sollte, etwas, von dem Sie vielleicht ausgehen können:

using System;

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

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

Denken Sie daran, dass Sie auf diese Weise keine vom Jitter erzeugten Typ- und Dateiladeausnahmen abfangen können. Sie treten auf, bevor Ihre Main()-Methode ausgeführt wird. Um diese abzufangen, müssen Sie den Jitter verzögern, den riskanten Code in eine andere Methode verschieben und das Attribut [MethodImpl(MethodImplOptions.NoInlining)] darauf anwenden.

25voto

BlueMonkMN Punkte 24177

Wenn Sie eine Single-Thread-Anwendung haben, können Sie ein einfaches try/catch in der Main-Funktion verwenden. Dies deckt jedoch keine Ausnahmen ab, die außerhalb der Main-Funktion, z. B. in anderen Threads, ausgelöst werden können (wie in anderen Kommentaren erwähnt). Dieser Code zeigt, wie eine Ausnahme dazu führen kann, dass die Anwendung beendet wird, obwohl Sie versucht haben, sie in der Main-Funktion zu behandeln (beachten Sie, wie das Programm ordnungsgemäß beendet wird, wenn Sie die Eingabetaste drücken und der Anwendung erlauben, ordnungsgemäß beendet zu werden, bevor die Ausnahme auftritt, aber wenn Sie es laufen lassen, wird es ziemlich unglücklich beendet):

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("Caught an 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;
   }
}

Sie können benachrichtigt werden, wenn ein anderer Thread eine Ausnahme auslöst, um einige Aufräumarbeiten durchzuführen, bevor die Anwendung beendet wird, aber soweit ich weiß, können Sie von einer Konsolenanwendung aus nicht erzwingen, dass die Anwendung weiterläuft, wenn Sie die Ausnahme auf dem Thread, von dem sie ausgelöst wird, nicht behandeln, ohne einige obskure Kompatibilitätsoptionen zu verwenden, damit sich die Anwendung so verhält, wie es bei .NET 1.x der Fall gewesen wäre:

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("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

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

Meiner Meinung nach ist die sauberste Art, damit umzugehen in einer Konsolenanwendung ist es, sicherzustellen, dass jeder Thread einen Exception-Handler auf der Root-Ebene hat:

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("Caught an 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("Caught an exception on the other thread");
   }
}

12voto

BlackICE Punkte 8598

Sie müssen auch Ausnahmen von Threads behandeln:

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

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

Whoop, sorry, das war für Winforms, für alle Threads, die Sie in einer Konsolenanwendung verwenden, müssen Sie in einem Try/Catch-Block einschließen. Hintergrund-Threads, die auf unbehandelte Ausnahmen stoßen, führen nicht zum Beenden der Anwendung.

1voto

Russell Jonakin Punkte 1586

Ich habe gerade eine alte VB.NET-Konsolenanwendung geerbt und musste einen globalen Exception Handler einrichten. Da diese Frage erwähnt VB.NET ein paar Mal und ist mit VB.NET getaggt, aber alle anderen Antworten hier sind in C#, ich dachte, ich würde die genaue Syntax für eine VB.NET-Anwendung als auch hinzufügen.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

Ich habe die REM Kommentarmarkierung anstelle des einfachen Anführungszeichens, da Stack Overflow die Syntaxhervorhebung anscheinend etwas besser mit REM .

-14voto

Rodney S. Foley Punkte 9774

Was Sie versuchen, sollte nach den MSDN-Dokumenten für .Net 2.0 funktionieren. Sie könnten auch versuchen, ein Try/Catch direkt in main um Ihren Einstiegspunkt für die Konsole app.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

Und jetzt wird Ihr Fang alles behandeln, was nicht gefangen wurde ( im Hauptstrang ). Es kann anmutig sein und sogar neu starten, wo es war, wenn Sie wollen, oder Sie können einfach die App sterben lassen und die Ausnahme protokollieren. Sie sollten ein finally hinzufügen, wenn Sie etwas aufräumen wollen. Jeder Thread benötigt seine eigene Ausnahmebehandlung auf hohem Niveau, ähnlich wie der Hauptthread.

Bearbeitet, um den von BlueMonkMN angesprochenen und in seiner Antwort ausführlich dargestellten Punkt bezüglich der Threads zu klären.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X