.NET - Was ist der beste Weg, um einen "Catch All Exceptions Handler" zu implementieren?

75

Ich frage mich, was der beste Weg ist, ein "wenn alles andere fehlschlägt, fangen Sie es" zu haben.

Ich meine, Sie behandeln so viele Ausnahmen wie möglich in Ihrer Anwendung, aber es gibt immer noch Fehler. Daher muss ich etwas haben, das alle nicht behandelten Ausnahmen abfängt, damit ich Informationen sammeln und in einer Datenbank speichern oder einreichen kann zu einem Webdienst.

Erfasst das Ereignis AppDomain.CurrentDomain.UnhandledException alles? Auch wenn die Anwendung Multithreading ist?

Randnotiz: Windows Vista stellt native API-Funktionen zur Verfügung, mit denen sich jede Anwendung nach einem Absturz selbst wiederherstellen kann. Ich kann mir den Namen jetzt nicht vorstellen. Ich möchte ihn jedoch lieber nicht verwenden, da viele unserer Benutzer ihn immer noch verwenden Windows XP.

TimothyP
quelle
2
Es ist die "Restart Manager" -Funktion in Windows Vista: danielmoth.com/Blog/2006/10/vista-restart-manager.html
huseyint
4
Ich schrieb einen ausführlichen Blog-Beitrag dazu: Ich muss sie alle fangen: Last-Chance-Ausnahmebehandlung in .NET mit WinForms
Jonathon Reinhart

Antworten:

33

Ich habe gerade mit AppDomains UnhandledException-Verhalten gespielt (dies ist die letzte Phase, in der die nicht behandelte Ausnahme registriert wird).

Ja, nach der Verarbeitung der Ereignishandler wird Ihre Anwendung beendet und der böse Dialog "... Programm funktioniert nicht mehr" angezeigt.

:) Das kannst du immer noch vermeiden.

Auschecken:

class Program
{
    void Run()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Console.WriteLine("Press enter to exit.");

        do
        {
            (new Thread(delegate()
            {
                throw new ArgumentException("ha-ha");
            })).Start();

        } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");


        Console.WriteLine("last good-bye");
    }

    int r = 0;

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Interlocked.Increment(ref r);
        Console.WriteLine("handled. {0}", r);
        Console.WriteLine("Terminating " + e.IsTerminating.ToString());

        Thread.CurrentThread.IsBackground = true;
        Thread.CurrentThread.Name = "Dead thread";            

        while (true)
            Thread.Sleep(TimeSpan.FromHours(1));
        //Process.GetCurrentProcess().Kill();
    }

    static void Main(string[] args)
    {
        Console.WriteLine("...");
        (new Program()).Run();
    }
}

PS Behandeln Sie die für Application.ThreadException (WinForms) oder DispatcherUnhandledException (WPF) nicht behandelten auf der höheren Ebene.

bohdan_trotsenko
quelle
Der Umgang mit der DispatcherUnhandledException löste meine Probleme, vergaß jedoch, die Frage zu aktualisieren. Danke für die Antwort
TimothyP
20
Ich denke, es ist fair zu erwähnen, dass der obige Code jeden fehlgeschlagenen Thread für immer herumliegen lässt. Das heißt, früher oder später werden viele Zombiefäden herumhängen. Persönlich finde ich das nicht sehr nützlich.
Brian Rasmussen
2
Genau. Das brachte Zombies hervor. In Trauer akzeptiere ich diese Alternative zum Beenden meiner Anwendung unmittelbar nach dem Beenden des Handlers. Bevor Sie den toten Thread für immer schlafen lassen, können Sie einen Bericht an Ihren Server senden, den Benutzer die anstehenden Aufgaben erledigen lassen und die Anwendung ordnungsgemäß beenden (oder neu starten).
Bohdan_Trotsenko
3
Diese Lösung ist so falsch und gleichzeitig unter bestimmten Umständen so notwendig :) +1
empi
1
Dies wird ausgelöst, wenn der Name des Threads bereits zuvor festgelegt wurde : Thread.CurrentThread.Name = "Dead thread".
FunctorSalad
17

In ASP.NET verwenden Sie die Application_ErrorFunktion in der Global.asaxDatei.

In WinForms verwenden Sie das MyApplication_UnhandledExceptionin der ApplicationEventsDatei

Diese beiden Funktionen werden aufgerufen, wenn in Ihrem Code eine nicht behandelte Ausnahme auftritt. Mit diesen Funktionen können Sie die Ausnahme protokollieren und dem Benutzer eine nette Nachricht präsentieren.

ine
quelle
13

Für Winform-Anwendungen verwende ich neben AppDomain.CurrentDomain.UnhandledException auch Application.ThreadException und Application.SetUnhandledExceptionMode (mit UnhandledExceptionMode.CatchException). Diese Kombination scheint alles zu fangen.

Bob Nadler
quelle
6
alles außer Ausnahmen für sekundäre Threads, Timer-Threads und Threadpool-Threads!
Steven A. Lowe
2
Ich bin damit einverstanden, dass die "UnhandledException" von AppDomain nicht ausreicht, wenn unter "Behandlung von Ausnahmen" tatsächlich eine Wiederherstellung verstanden wird. Die ursprüngliche Frage schien jedoch nur auf die Fehlerberichterstattung und nicht auf die Wiederherstellung ausgerichtet zu sein. Allein zu diesem Zweck würde es ausreichen.
Christian.K
1
@Steven: Wenn der Thread-Pool-Job über BeginInvoke gestartet wird, wird beim Aufruf von EndInvoke eine Ausnahme von diesem Thread an den aufrufenden Thread weitergeleitet.
Brian Rasmussen
6

Im Haupt-Thread haben Sie folgende Optionen:

Für andere Themen:

  • Sekundäre Threads haben keine unbehandelten Ausnahmen. Verwenden Sie SafeThread
  • Arbeiter-Threads: (Timer, Threadpool) Es gibt überhaupt kein Sicherheitsnetz!

Beachten Sie, dass diese Ereignisse nicht behandeln Ausnahmen, sie nur berichten sie an die Anwendung - oft , wenn es viel zu spät ist , etwas Sinnvolles / sane über sie zu tun

Das Protokollieren von Ausnahmen ist gut, aber das Überwachen von Anwendungen ist besser ;-)

Vorsichtsmaßnahme: Ich bin der Autor des SafeThread- Artikels.

Steven A. Lowe
quelle
@ StevenA.Lowe nicht vollständige Quellcode-Sicherung über CALM?
Kiquenet
4

Vergessen Sie bei WinForms nicht, auch das nicht behandelte Ausnahmeereignis des aktuellen Threads anzuhängen (insbesondere, wenn Sie Multi-Threading verwenden).

Einige Links zu Best Practices hier und hier und hier (wahrscheinlich der beste Artikel zur Ausnahmebehandlung für .net)

Bogdan Maxim
quelle
Bitte verlinken Sie auf einen Verweis auf das "aktuelle unbehandelte Ausnahmeereignis des Threads". Ich hatte den Eindruck, dass es so etwas nicht gab!
Steven A. Lowe
2

Es gibt auch eine coole Sache namens ELMAH , die alle ASP.NET-Fehler protokolliert, die in einer Webanwendung auftreten. Ich weiß, dass Sie nach einer Winform App-Lösung fragen, aber ich war der Meinung, dass dies für jeden von Vorteil sein könnte, der solche Dinge in einer Web-App benötigt. Wir verwenden es dort, wo ich arbeite, und es war beim Debuggen sehr hilfreich (insbesondere auf Produktionsservern!)

Hier sind einige Funktionen, die es hat (direkt von der Seite gezogen):

  • Protokollierung fast aller nicht behandelten Ausnahmen.
  • Eine Webseite zum Remote-Anzeigen des gesamten Protokolls der neu codierten Ausnahmen.
  • Eine Webseite, auf der Sie die vollständigen Details einer protokollierten Ausnahme aus der Ferne anzeigen können.
  • In vielen Fällen können Sie den ursprünglichen gelben Todesbildschirm überprüfen, den ASP.NET für eine bestimmte Ausnahme generiert hat, auch wenn der customErrors-Modus deaktiviert ist.
  • Eine E-Mail-Benachrichtigung über jeden Fehler zum Zeitpunkt seines Auftretens.
  • Ein RSS-Feed der letzten 15 Fehler aus dem Protokoll.
  • Eine Reihe von Sicherungsspeicherimplementierungen für das Protokoll, einschließlich In-Memory, Microsoft SQL Server und mehrere von der Community bereitgestellte.
Ryan Abbott
quelle
Es spielt keine Rolle, dass ich nach Nicht-Web-Apps gefragt habe, dies sind immer noch ziemlich nützliche Informationen für andere! also +1
TimothyP
2

Sie können die meisten Ausnahmen in diesem Handler auch in Multithread-Apps überwachen. In .NET (beginnend mit 2.0) können Sie jedoch nicht behandelte Ausnahmen nur dann abbrechen, wenn Sie den Kompatibilitätsmodus 1.1 aktivieren. In diesem Fall wird die AppDomain auf jeden Fall heruntergefahren. Das Beste, was Sie tun können, ist, die App in einer anderen AppDomain zu starten, damit Sie diese Ausnahme behandeln und eine neue AppDomain erstellen können, um die App neu zu starten.

jezell
quelle
0

Ich verwende den folgenden Ansatz, der funktioniert und die Menge an Code erheblich reduziert (ich bin mir jedoch nicht sicher, ob es einen besseren Weg gibt oder welche Fallstricke dies haben könnte. Wann immer Sie anrufen: Ich frage, ob die Fragen, die Minuspunkte geben, höflich wären genug, um ihre Handlungen zu klären;)

try 
{
    CallTheCodeThatMightThrowException()
 }
catch (Exception ex)
{
    System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
    Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch

Und hier ist der ErrorHandler-Code: Nur um es klar zu machen: objUser - ist das Objekt, das die Benutzer modelliert (Sie erhalten möglicherweise Informationen wie Domänenname, Abteilung, Region usw. für Protokollierungszwecke ILog Logger - ist das Protokollierungsobjekt - z. B. dasjenige Ausführen der Protokollierungsaktivitäten StackTrace st - Das StackTrace-Objekt, mit dem Sie Debugging-Informationen für Ihre App erhalten

using System;
using log4net; //or another logging platform

namespace GenApp.Utils
{
  public class ErrorHandler
  {
    public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex )
    {
      if (ex is NullReferenceException)
      { 
      //do stuff for this ex type
      } //eof if

      if (ex is System.InvalidOperationException) 
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.IndexOutOfRangeException) 
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.Data.SqlClient.SqlException)
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.FormatException)
      {
        //do stuff for this ex type
      } //eof if

      if (ex is Exception)
      {
        //do stuff for this ex type
      } //eof catch

    } //eof method 

  }//eof class 
} //eof namesp
Yordan Georgiev
quelle
Dadurch werden nur Ausnahmen für den aktuellen Thread abgefangen. Das heißt, es nützt wenig, wenn CallTheCodeThatMightThrowException andere Threads erstellt oder verwendet.
Brian Rasmussen
0

In einer verwalteten GUI-App werden Ausnahmen, die vom GUI-Thread stammen, standardmäßig von allem behandelt, was der Application.ThreadException zugewiesen ist.

Ausnahmen, die von den anderen Threads stammen, werden von AppDomain.CurrentDomain.UnhandledException behandelt.

Wenn Sie möchten, dass Ihre GUI-Thread-Ausnahmen genauso funktionieren wie Ihre Nicht-GUI-Ausnahmen, damit sie von AppDomain.CurrentDomain.UnhandledException behandelt werden, können Sie Folgendes tun:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

Ein Vorteil beim Abfangen der GUI-Thread-Ausnahmen mithilfe von ThreadException besteht darin, dass Sie der Verwendung die Optionen geben können, die App fortzusetzen. Um sicherzustellen, dass keine Konfigurationsdateien das Standardverhalten überschreiben, können Sie Folgendes aufrufen:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Sie sind immer noch anfällig für Ausnahmen von schlecht benommenen nativen DLLs. Wenn eine native DLL ihren eigenen Handler mit Win32 SetUnhandledExceptionFilter installiert, soll sie den Zeiger auf den vorherigen Filter speichern und auch aufrufen. Wenn dies nicht der Fall ist, wird Ihr Handler nicht angerufen.

Corey Trager
quelle