Destruktoren, die nicht aufgerufen werden, wenn eine native (C ++) Ausnahme an die CLR-Komponente weitergegeben wird

8

Wir haben eine große Menge nativen C ++ - Code, der in DLLs kompiliert ist.

Dann haben wir ein paar DLLs, die C ++ / CLI-Proxy-Code enthalten, um die C ++ - Schnittstellen zu umbrechen.

Außerdem haben wir C # -Code, der in C ++ / CLI-Wrapper aufruft.

Standard Sachen, soweit.

Aber wir haben viele Fälle, in denen native C ++ - Exceptions in die .Net-Welt propagieren dürfen und wir uns auf die Fähigkeit von .Net verlassen, diese als System.Exception-Objekte einzubinden, und das funktioniert größtenteils gut.

>

Wir haben jedoch festgestellt, dass Destruktoren von Objekten im Bereich zum Zeitpunkt des Wirkens nicht aufgerufen werden, wenn die Ausnahme propagiert wird!

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 Optimierungen 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) eingebunden wird?

    
philsquared 23.03.2010, 18:27
quelle

4 Antworten

3

Die MSDN-Seite für den E-Compiler-Schalter besagt dieses Verhalten: -

Ссылка

Hier ist das relevante Zitat: -

  

Wenn Sie / EHs verwenden, dann fangen Sie an   Klausel wird nicht asynchron fangen   Ausnahmen. In Visual C ++ 2005   alle Objekte im Umfang, wenn der   asynchrone Ausnahme wird generiert   wird nicht zerstört werden, auch wenn die   asynchrone Ausnahme wird behandelt.

Grundsätzlich / EHsc ist die optimistische Ansicht - sie geht davon aus, dass die einzigen Ausnahmen echte C ++ - Varianten sind und entsprechend optimiert werden. / 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 "bestmöglichen Versuch" anbieten wollen, dann wird / EHa notwendig sein.

Ich stimme auch den Meinungen von @ JaredPar zu, dass Ausnahmen keine Modulgrenzen überschreiten dürfen. 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.

    
Chris Oldwood 23.03.2010 21:51
quelle
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.

Wie Sie bereits festgestellt haben, sind die besten zwei Optionen

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

Idealerweise solltest du den zweiten sowieso machen. Nach meiner Erfahrung ist es eine schlechte Übung, C ++ - Ausnahmen Komponentengrenzen zu überlassen.

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

    
JaredPar 23.03.2010 18:35
quelle
2

Du machst das nicht richtig, denke ich. Mit _set_se_translator () müssen Sie bereits mit / EHa kompilieren. Auf der MSDN Library-Seite :

%Vor%

Im Ernst: Sie werden verwalteten Code brechen, wenn Sie ihn 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 try Block.

    
Hans Passant 23.03.2010 19:38
quelle
1

Es gibt einen Fehler in der 2.0-Version der CLR, der 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.

Siehe Boost Shared Mutex nicht freigegeben, nachdem eine Ausnahme ausgelöst wurde für Details.

    
roken 19.10.2011 20:00
quelle