2010-03-23 8 views
8

Wir haben eine große Menge nativen C++ - Code, der in DLLs kompiliert ist.Destruktoren, die nicht aufgerufen werden, wenn die native (C++) Ausnahme zur CLR-Komponente weitergeleitet wird

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

Darüber hinaus haben wir C# -Code in die C++/CLI-Wrapper aufrufen.

Standard-Zeug, so weit.

Aber wir haben viele Fälle, in denen native C++ - Ausnahmen in die .Net-Welt propagieren dürfen und wir .Nets Fähigkeit, diese als System.Exception-Objekte zu umhüllen, und größtenteils funktioniert das gut.

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

Nach einigen Recherchen haben wir festgestellt, dass dies ein ziemlich bekanntes Problem ist. Die Lösungen/Problemumgehungen scheinen jedoch weniger konsistent zu sein. Wir haben festgestellt, dass das Problem verschwindet, wenn der native Code mit/EHa anstelle von/EHsc kompiliert wird (zumindest in unserem Testfall). Allerdings würden wir lieber/EHsc verwenden, da wir SEH-Exceptions selbst in C++ - Exceptions übersetzen, und wir würden dem Compiler mehr Spielraum für die Optimierung geben.

Gibt es andere Problemumgehungen für dieses Problem - abgesehen davon, dass jeder Aufruf über die native verwaltete Grenze in einem (nativen) try-catch-throw (zusätzlich zur C++/CLI-Ebene) eingeschlossen wird?

+0

hast du die richtige antwort dafür gefunden –

+0

kann ich mich leider nicht mehr erinnern. – philsquared

Antwort

2

Leider nein, ich glaube nicht, dass es gute Workarounds gibt. Die C++ - Ausnahmeimplementierung auf MS-Plattformen wird mithilfe von SEH-Ausnahmen (IIRC) implementiert. Die CLR greift in die SEH-Verarbeitung ein, um systemeigene Ausnahmen abzufangen und sie in CLR-Ausnahmen zu verarbeiten. Da sie auf einer SEH-Ebene abgefangen wird, sieht die Ausnahme wie eine SEH-Ausnahme zu C++ aus, und Destruktoren werden entsprechend ausgeführt oder nicht ausgeführt.

So wie Sie die besten zwei Optionen festgestellt haben, sind

  • Compile mit/EHa
  • einen try/catch am Eintritts- und Austrittspunkt Ihrer Funktionen

Idealer hinzufügen Sie sollte sowieso den zweiten tun. Nach meiner Erfahrung ist es eine schlechte Übung, C++ - Ausnahmen Komponentengrenzen zu überlassen.

Es ist auch wahrscheinlich eine Hacky-Lösung, die Sie mit _set_seh_translator (Documentation) erreichen könnten. Ich empfehle jedoch, diese Funktion zu vermeiden, da sie die Behandlung von CLR-Ausnahmebedingungen versehentlich unterlaufen kann und viele unerwünschte Probleme verursacht.

+0

Wir verwenden tatsächlich SetUnhandledExceptionFilter() (siehe meine Antwort auf @Nobugz) und wir berücksichtigen CLR-Ausnahmen in dieser Behandlung. Wie bei C++ - Ausnahmen, die Komponentengrenzen überschreiten: (1) Das Versprechen war, dass dies automatisch von uns zur Laufzeit behandelt würde - und anders als dieses Problem ist es. (2) Wenn wir es tun würden, wäre der logische Platz in unserem C++/CLI-Proxy-Code. Da es sich jedoch bereits um CLI-Code handelt, ist es zu spät, um dieses Problem zu beheben. Um also diese Route zu durchlaufen, müssten wir effektiv zwei Wrapping-Schichten haben (eine native, eine verwaltete)! – philsquared

2

Sie tun das nicht richtig, denke ich. Mit _set_se_translator() müssen Sie bereits mit/EHa kompilieren. Von der MSDN Library page:

You must use /EHa when using _set_se_translator. 

Noch gravierender ist, werden Sie brechen Code verwaltet werden, wenn Sie es verwenden. Die CLR verlässt sich auf SEH-Ausnahmen, um verschiedene Pannen zu erkennen. Es verwendet SetUnhandledExceptionFilter, um sie zu fangen. Insbesondere NullReferenceException und OverflowException (x86) werden auf diese Weise ausgelöst. Wenn Sie einen eigenen __try-Block injizieren, verhindern Sie, dass diese Ausnahme in die CLR fließt.Obwohl Sie es "behandeln" werden Sie keine Spur des genauen Grundes für die Ausnahme haben. Und verwaltete try/catch-Blöcke können es nicht erkennen.

Eine Heilung für/EHa Effizienz (wenn es tatsächlich ein Problem ist) ist 64-Bit-Code. Sein funktionstabellenbasierter Stapelabwicklungsvorgang ist sehr effizient mit Null Overhead für einen Testblock.

+0

Wir verwenden SetUnhandledExceptionFilter() selbst. Wir ketten auf früher registrierte Filter, wenn also ein Handler auf der .Net-Seite des Zauns ist, der immer noch hineinschaut (was in dem Fall passiert, in dem die Frage ausgelöst wurde). Die Verwendung von 64-Bit-Code ist für uns keine Option, da dies in einem Framework geschieht, das auf Desktops ausgeführt werden kann (wo unser aktueller Standard XP ist). – philsquared

3

Die MSDN-Seite für den/E-Compiler-Schalter funktioniert Zustand dieses Verhalten: -

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

Hier ist der relevante Zitat: -

Wenn Sie/EHs, dann fangen Klausel nicht asynchrone Ausnahmen fangen. Außerdem werden in Visual C++ 2005, alle Objekte im Bereich, wenn die asynchrone Ausnahme generiert wird nicht zerstört, auch wenn die asynchrone Ausnahme behandelt wird.

Grundsätzlich/EHsc ist die optimistische Sicht - es wird davon ausgegangen, dass die einzigen Ausnahmen wahr C++ Stil diejenigen sind und entsprechend optimieren./EHa hingegen nimmt die pessimistische Sicht und geht davon aus, dass jede Codezeile eine Ausnahme erzeugen könnte.

Wenn Sie garantieren können, dass Sie niemals eine Zugriffsverletzung oder einen In-Page-Fehler oder eine andere SEH verursachen, verwenden Sie/EHsc. Aber wenn Sie einen Dienst schreiben und/oder einen "besten Versuch" machen wollen, dann wird/EHa notwendig sein.

Ich stimme auch mit @ JaredPar Gefühle über Ausnahmen nicht zu Modulgrenzen zu überschreiten. Was @nobugz über die Art und Weise sagt, in der die CLR Ausnahmen behandelt, mag wahr sein, aber ich denke, es gibt einen Unterschied zwischen .Net-Code, der direkt zu nativem Code über P/Invoke aufruft und in eine C++/CLI-Interop-DLL aufruft. Im ersten Fall muss die CLR die Situation in Ihrem Namen behandeln, während Sie im letzteren Fall die Kontrolle haben und entsprechend übersetzen können.

+0

Danke Chris. Allerdings ist der Fall in meiner Frage mit C++ Ausnahmen. Zum Überschreiten der Modulgrenzen werde ich die Antwort von @ JaredPar kommentieren. – philsquared

+0

Sorry, ich habe mich auf das/EHa-Bit konzentriert. Sagst du, dass du es sogar mit einem normalen Wurf siehst? z.B.werf std :: runtime_error(); –

+0

ja. Auch mit 'throw 42;' – philsquared

1

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

Weitere Informationen finden Sie unter Boost shared mutex not released after exception thrown.

+0

Siehe auch http://connect.microsoft.com/VisualStudio/feedback/details/382860/ehsc-eha-stack-unwinding –

Verwandte Themen