13

Im Zusammenhang mit this question möchte ich CLR erzwingen, dass meine .NET 4.5.2-Anwendung beschädigte Statusausnahmen abruft, nur um sie zu protokollieren und dann die Anwendung zu beenden. Was ist der richtige Weg, dies zu tun, wenn ich catch (Exception ex) an mehreren Stellen in der App habe?Korrekte Behandlung von Ausnahmefehlern des beschädigten Zustands

Also, nachdem ich das <legacyCorruptedStateExceptionsPolicy> Attribut angegeben habe, wenn ich richtig verstanden habe, werden alle catch (Exception ex) Handler Ausnahmen wie AccessViolationException fangen und glücklich weiter.

Ja, ich weiß, catch (Exception ex) ist eine schlechte Idee ™, aber wenn CLR würde mindestens die richtige Stack-Trace in das Ereignisprotokoll, wäre ich mehr als glücklich, dem Kunden zu erklären, dass seine Server App schnell um 1 Uhr morgens und offline für die Nacht zu sein ist eine gute Sache. Aber unglücklicherweise zeichnet CLR eine unrelated exception im Ereignisprotokoll auf und schließt dann den Prozess, so dass ich nicht herausfinden kann, was tatsächlich passiert ist.

Die Frage ist, wie man damit dies geschieht, Prozess breit:

if the exception thrown is a Corrupted State Exception: 
    - write the message to the log file 
    - end the process 

(Update)

Mit anderen Worten, dies ist wahrscheinlich für die meisten Ausnahmen in einer einfachen App funktionieren würde:

[HandleProcessCorruptedStateExceptions] 
[SecurityCritical] 
static void Main() // main entry point 
{ 
    try 
    { 

    } 
    catch (Exception ex) 
    { 
     // this will catch CSEs 
    } 
} 

Aber, wird es nicht für Arbeit:

  • Unbehandelte Anwendungsdomänenausnahmen (d. H. geworfen auf nicht-Vordergrund-Threads)
  • Windows Service-Anwendungen (den don keinen tatsächlichen Main Einstiegspunkt haben)

So scheint es, wie <legacyCorruptedStateExceptionsPolicy> der einzige Weg ist, diese Arbeit zu machen, in welchem ​​Fall mich weiß nicht, wie man nach dem Loggen des CSE scheitert?

Antwort

9

Statt <legacyCorruptedStateExceptionsPolicy> der Verwendung wäre es besser, [HandleProcessCorruptedStateExceptions] zu verwenden (und [SecurityCritical]), wie hier angegeben:

https://msdn.microsoft.com/en-us/magazine/dd419661.aspx

Im Anschluss daran Ihre Main Methode wie folgt aussehen sollte:

[HandleProcessCorruptedStateExceptions, SecurityCritical] 
static void Main(string[] args) 
{ 
    try 
    { 
     ... 
    } 
    catch (Exception ex) 
    { 
     // Log the CSE. 
    } 
} 

Aber beachten Sie, dass dies die schwerwiegenderen Ausnahmen wie StackOverflowException und ExecutionEngineException nicht erfasst.

Auch finally der beteiligten try Blöcke werden nicht ausgeführt:

https://csharp.2000things.com/2013/08/30/920-a-finally-block-is-not-executed-when-a-corrupted-state-exception-occurs/

Für andere nicht behandelte Appdomain Ausnahmen können Sie:

  • AppDomain.CurrentDomain.UnhandledException
  • Application.Current.DispatcherUnhandledException
  • TaskScheduler.UnobservedTaskException

(Bitte suchen Sie nach den Details, wenn ein bestimmter Handler für Ihre Situation geeignet ist. TaskScheduler.UnobservedTaskException zum Beispiel ist ein bisschen schwierig)

Wenn Sie den Zugriff auf das Verfahren nicht Main haben, können Sie auch Ihre AppDomain Exception-Handler markieren die CSE zu fangen.

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 

... 

[HandleProcessCorruptedStateExceptions, SecurityCritical] 
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
{ 
    // AccessViolationExceptions will get caught here but you cannot stop 
    // the termination of the process if e.IsTerminating is true. 
} 

Die letzte Verteidigungslinie könnte ein nicht verwalteter UnhandledExceptionFilter so aussehen:

[DllImport("kernel32"), SuppressUnmanagedCodeSecurity] 
private static extern int SetUnhandledExceptionFilter(Callback cb); 
// This has to be an own non generic delegate because generic delegates cannot be marshalled to unmanaged code. 
private delegate uint Callback(IntPtr ptrToExceptionInfo); 

Und irgendwo am Anfang des Prozesses dann:

SetUnhandledExceptionFilter(ptrToExceptionInfo => 
{ 
    var errorCode = "0x" + Marshal.GetExceptionCode().ToString("x2"); 
    ... 
    return 1; 
}); 

Sie können mehr Informationen über die möglichen Rückgabecodes finden Sie hier:

https://msdn.microsoft.com/en-us/library/ms680634(VS.85).aspx

Eine „Spezialität“ des UnhandledExceptionFilter ist, dass es nicht, wenn ein Debugger angeschlossen ist aufgerufen wird. (Zumindest nicht in meinem Fall eine WPF-App zu haben.) Also sei dir dessen bewusst.

Wenn Sie alle entsprechenden ExceptionHandler von oben festlegen, sollten Sie alle Ausnahmen protokollieren, die protokolliert werden können. Für die ernsteren Ausnahmen (wie StackOverflowException und ExecutionEngineException) müssen Sie einen anderen Weg finden, weil der ganze Prozess unbrauchbar ist, nachdem sie passiert sind. Ein möglicher Weg könnte ein anderer Prozess sein, der den Hauptprozess überwacht und fatale Fehler protokolliert.

Zusätzliche Hinweise:

+0

Danke, also bedeutet das, dass ich die Handler-Methode 'AppDomain.CurrentDomain.UnhandledException' mit' [HandleProcessCorruptedStateExceptions] 'dekorieren kann und CSEs richtig erfasst werden? – Lou

+1

Ja, das kannst du definitiv. Ich werde meine Antwort bearbeiten, um diesen Aspekt zu unterstreichen. – haindl

5

Dank für den Hinweis auf @haindl, dass Sie auch Behandlermethoden mit dem [HandleProcessCorruptedStateExceptions] Attribut schmücken können, so habe ich ein wenig Test-App nur um zu bestätigen, wenn die Dinge wirklich funktionieren, wie sie sollen.

Hinweis: meisten Antworten sagen, dass ich auch das [SecurityCritical] Attribut enthalten soll, obwohl in den Tests unter Weglassen es nicht das Verhalten geändert hat (die [HandleProcessCorruptedStateExceptions] allein schien ganz gut zu funktionieren). Allerdings werde ich beide Attribute unten lassen, da ich annehme, dass all diese Leute wussten, was sie sagten. Das ist ein Schulbeispiel für "Copied from StackOverflow" -Muster in Aktion.

Die Idee ist, offensichtlich, um zu entfernen die <legacyCorruptedStateExceptionsPolicy> Einstellung von app.config, das heißt nur unser äußerstes ermöglichen (Einstiegs-) -Prozedur (s) zum Abfangen der Ausnahme, melden Sie es, und dann scheitern. Das Hinzufügen der Einstellung ermöglicht es Ihrer App, fortzufahren, wenn Sie die Ausnahme in einem inneren Handler abfangen, und dies ist nicht was Sie wollen: Die Idee ist nur, um die genaue Ausnahme Info zu erhalten und dann kläglich sterben.

verwendete ich die following method die Ausnahme zu werfen:

static void DoSomeAccessViolation() 
{ 
    // if you have any questions about why this throws, 
    // the answer is "42", of course 

    var ptr = new IntPtr(42); 
    Marshal.StructureToPtr(42, ptr, true); 
} 

1. Fangen Ausnahmen von Main:

[SecurityCritical] 
[HandleProcessCorruptedStateExceptions] 
static void Main(string[] args) 
{ 
    try 
    { 
     DoSomeAccessViolation(); 
    } 
    catch (Exception ex) 
    { 
     // this will catch all CSEs in the main thread 
     Log(ex); 
    } 
} 

2. alle Ausnahmen Fangen, einschließlich Hintergrund-Threads/Aufgaben:

// no need to add attributes here 
static void Main(string[] args) 
{ 
    AppDomain.CurrentDomain.UnhandledException += UnhandledException; 

    // throw on a background thread 
    var t = new Task(DoSomeAccessViolation); 
    t.Start(); 
    t.Wait(); 
} 

// but it's important that this method is marked 
[SecurityCritical] 
[HandleProcessCorruptedStateExceptions] 
private static void UnhandledException(object sender, UnhandledExceptionEventArgs e) 
{ 
    // this will catch all unhandled exceptions, including CSEs 
    Log(e.ExceptionObject as Exception); 
} 

Ich würde empfehlen, nur den letzteren Ansatz zu verwenden, und entfernen Sie die [HandleProcessCorruptedStateExceptions] von alle anderen Orte, um sicherzustellen, dass die Ausnahme nicht an der falschen Stelle gefangen wird. I.e. Wenn Sie irgendwo einen try/catch Block haben und ein AccessViolationException geworfen wird, möchten Sie, dass CLR den catch Block überspringt und an den UnhandledException weitergibt, bevor Sie die App beenden.

Verwandte Themen