9 Stimmen

Destruktoren werden nicht aufgerufen, wenn sich eine native (C++) Ausnahme auf eine CLR-Komponente ausbreitet

Wir haben einen großen Bestand an nativem C++-Code, der in DLLs kompiliert ist.

Dann haben wir einige DLLs, die C++/CLI-Proxy-Code enthalten, um die C++-Schnittstellen zu umhüllen.

Hinzu kommt, dass wir C#-Code haben, der die C++/CLI-Wrapper aufruft.

So weit das Übliche.

Aber wir haben viele Fälle, in denen native C++-Ausnahmen in die .Net-Welt übertragen werden dürfen, und wir verlassen uns auf die Fähigkeit von .Net, diese als System.Exception-Objekte zu verpacken, was in den meisten Fällen gut funktioniert.

Wir haben jedoch festgestellt, dass die Destruktoren von Objekten, die sich zum Zeitpunkt des Wurfs im Anwendungsbereich befinden, nicht aufgerufen werden, wenn sich die Ausnahme ausbreitet!

Nach einigen Nachforschungen haben wir herausgefunden, dass dies ein ziemlich bekanntes Problem ist. Die Lösungen/Workarounds scheinen jedoch weniger einheitlich zu sein. Wir haben herausgefunden, dass das Problem verschwindet, wenn der native Code mit /EHa anstelle von /EHsc kompiliert wird (zumindest in unserem Testfall war es so). Wir würden es jedoch vorziehen, /EHsc zu verwenden, da wir selbst SEH-Ausnahmen in C++-Ausnahmen übersetzen und dem Compiler lieber mehr Spielraum für Optimierungen lassen würden.

Gibt es andere Workarounds für dieses Problem - andere als Wrapping jeden Aufruf über die native-verwaltete Grenze in einem (native) Try-Catch-Wurf (zusätzlich zu der C++/CLI-Schicht)?

3voto

JaredPar Punkte 699699

Leider nein, ich glaube nicht, dass es irgendwelche guten Abhilfen gibt. Die C++-Ausnahme-Implementierung auf MS-Plattformen sind mit SEH-Ausnahmen (IIRC) implementiert. Die CLR Haken in SEH Behandlung zu fangen native Ausnahmen und verarbeiten sie in CLR-Ausnahmen. Da sie auf SEH-Ebene abgefangen werden, sieht die Ausnahme für C++ wie eine SEH-Ausnahme aus und Destruktoren werden entsprechend ausgeführt oder nicht ausgeführt.

Wie Sie bereits festgestellt haben, sind die beiden besten Optionen

  • Kompilieren mit /EHa
  • Fügen Sie ein try/catch am Eingangs- und Ausgangspunkt Ihrer Funktionen hinzu

Idealerweise sollten Sie den zweiten Schritt auf jeden Fall machen. Meiner Erfahrung nach gilt es als schlechte Praxis, C++-Ausnahmen über Komponentengrenzen hinweg zuzulassen.

Wahrscheinlich gibt es auch eine einfache Lösung, die Sie mit _set_seh_translator ( Dokumentation ). Ich empfehle jedoch dringend, diese Funktion zu vermeiden, da sie ungewollt die CLR-Ausnahmebehandlung untergraben und eine Menge unerwünschter Probleme verursachen kann.

3voto

Hans Passant Punkte 894572

Ich glaube, Sie machen das nicht richtig. Die Verwendung von _set_se_translator() erfordert bereits, dass Sie mit /EHa kompilieren. Von der MSDN-Bibliotheksseite :

You must use /EHa when using _set_se_translator.

Noch schlimmer ist, dass Sie den verwalteten Code brechen werden, wenn Sie ihn verwenden. Die CLR stützt sich auf SEH-Ausnahmen, um verschiedene Fehler zu erkennen. Sie verwendet SetUnhandledExceptionFilter, um sie abzufangen. Insbesondere NullReferenceException und OverflowException (x86) werden auf diese Weise ausgelöst. Wenn Sie Ihren eigenen __try-Block einfügen, verhindern Sie, dass diese Ausnahmen in die CLR einfließen. Obwohl Sie sie "behandeln", werden Sie keine Rückschlüsse auf den genauen Grund der Ausnahme haben. Und verwaltete try/catch-Blöcke können sie nicht erkennen.

Ein Mittel gegen die /EHa-Effizienz (falls es tatsächlich ein Problem ist) ist 64-Bit-Code. Die auf Funktionstabellen basierende Stapelabwicklung ist sehr effizient und verursacht keinen Overhead für einen Try-Block.

3voto

Chris Oldwood Punkte 1022

Die MSDN-Seite für den Compilerschalter /E gibt dieses Verhalten an: -

http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx

Hier ist das entsprechende Zitat:-

I Klausel keine asynchronen Ausnahmen. Außerdem werden in Visual C++ 2005, alle Objekte im Anwendungsbereich, wenn die asynchrone Ausnahme erzeugt wird werden nicht d asynchrone Ausnahme behandelt wird.

Im Grunde ist /EHsc die optimistische Sichtweise - es geht davon aus, dass die einzigen Ausnahmen echte C++-Ausnahmen sind und optimiert entsprechend. /EHa hingegen vertritt die pessimistische Sichtweise und geht davon aus, dass jede Codezeile zu einer Ausnahme führen kann, die erzeugt wird.

Wenn Sie garantieren können, dass Sie niemals eine Zugriffsverletzung, einen In-Page-Fehler oder einen anderen SEH verursachen werden, dann verwenden Sie /EHsc. Wenn Sie jedoch einen Dienst schreiben und/oder eine "bestmögliche Leistung" erbringen wollen, ist /EHa erforderlich.

Ich stimme auch mit @JaredPar überein, dass Ausnahmen keine Modulgrenzen überschreiten dürfen. Was @nobugz über die Art und Weise sagt, wie die CLR Ausnahmen behandelt, mag wahr sein, aber ich denke, es gibt einen Unterschied zwischen .Net-Code, der mit P/Invoke direkt nativen Code aufruft, und dem Aufruf einer C++/CLI-Interop-DLL. Im ersten Fall muss die CLR die Situation in Ihrem Namen behandeln, während Sie im zweiten Fall die Kontrolle haben und entsprechend übersetzen können.

1voto

roken Punkte 3846

Es gibt einen Fehler in der Version 2.0 der CLR, der dieses Problem verursacht. Wenn Sie Ihre verwaltete ausführbare Datei mit der 4.0 CLR ausführen, können die Destruktoren wie erwartet aufgerufen werden.

Siehe Gemeinsam genutzter Mutex von Boost wird nach einer ausgelösten Ausnahme nicht freigegeben für Einzelheiten.

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