C++ Exceptions verlassen nicht die DLL?
-
SEH und C++ Exceptions sind ja erstmal zwei völlig unterschiedliche Dinge. SEH ist Funktion der Win32 API, dementsprechend ausschließlich in Windows verfügbar und nicht an eine bestimmte Sprache Gebunden. In C/C++ kann man SEH über die proprietären Erweiterungen**
__try
and__except
**verwenden - zumindest wenn man ausschließlich für Windows entwickelt und ausschließlich den Microsoft Compiler verwendet. C++ Exceptions hingegen sind ein Sprach-Feature von C++ und daher unter allen Compilern auf allen Plattformen verfügbar. Microsoft sagt dazu selbst "Although Windows and Visual C++ support structured exception handling (SEH), we recommend that you use ISO-standard C++ exception handling because it makes code more portable and flexible."Wie der jeweilige Compiler die C++ Exceptions technisch umsetzt bleibt jedoch allein dem jeweiligen Compiler überlassen. Selbst dann, wenn der Microsoft Compiler und die Windows System DLL's so abgestimmt sind, dass C++ Exceptions aus Modulen, die mit MSVC kompiliert wurden (dass C++ Exceptions bei MSVC mittels SEH behandelt werden hab ich allerdings noch nirgendwo gehört, hast Du da eine Quelle?), problemlos "durch gepumpt" werden, hilft das ja nur sehr begrenzt! Weiterhin müssten sowohl das Modul, aus dem die Exception "geworfen" wird, als auch das Modul, wo sie "gefangen" werden soll, mit MSVC kompiliert worden sein und es darf sich keine einzige "fremde" DLL - zum Beispiel eine die mit einem aktuellen MinGW/GCC kompiliert wurde - auf dem Callstack "dazwischen" befinden. Ein aktuelles MinGW/GCC verwendet z.B. standardmäßig "Dwarf-2" Exception-Handling, was definitiv nicht mit MSVC kompatibel ist.
-
Artchi schrieb:
DeathCubeUK! Bitte erstmal mein Posting lesen, bevor du mit Standard-Antworten kommst. Deshalb hatte ich in meinem Posting die "Anmerkung" mit gegeben, weil ich deinen "Rat" schon tausendfach im Internet gefunden habe, wenn ich die Begriffe "C++ Exception DLL" in meine bevorzugte Suchmaschine eingebe. Weil überall wird nur abgeraten, selbst wenn die Poster deutlich schreiben, das sie eigene interne DLL-Projekte haben. So weit war ich also auch schon.
Wie in meinem Post beschrieben, ist für die "sichere" Übergabe von Exceptions von einem Modul A zu einem anderen Modul B zwar eine notwendige Voraussetzung, dass beide Module mit dem selben C++ Compiler übersetzt wurden und dass beide Module die selbe CRT-DLL benutzen - das alleine ist eben aber noch nicht ausreichend, weil es da noch mehr Fallstricke gibt!
-
Freund DeathCubeUK...
dass C++ Exceptions bei MSVC mittels SEH behandelt werden hab ich allerdings noch nirgendwo gehört, hast Du da eine Quelle?
Steht z.B. hier im ersten Satz:
http://blogs.msdn.com/b/zhanli/archive/2010/06/25/structured-exception-handling-seh-and-c-exception-handling.aspx
Oder auch...
http://support.microsoft.com/kb/185294
http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspxDavon abgesehen, hast du wirklich den Eindruck dich besser mit dem Thema SEH und C++ Exception Handling auszukennen als ich, so dass du mich "belehren" müsstest?
-
DeathCubeUK! Schön und gut was du alles schreibst. Aber das ist absolut am Thema vorbei! Ich habe nicht das Problem, das ich falsche/kaputte Exceptions oder Abstürze oder oder bekomme. Nein, ich bekomme NICHTS!
Kannst du mir nun helfen, das ich überhaupt etwas außerhalb der DLL mitbekomme oder nicht? Wenn ich einen Absturz bekommen würde, wäre das mehr als NICHTS.
Kannst du überhaupt zu meinem Thema etwas sagen, außer das es Gefahren birgt? Die einzigen die verstanden haben, was mein Problem ist, ist Th69 und Hustbaer.
-
DeathCubeUK schrieb:
Wie in meinem Post beschrieben, ist für die "sichere" Übergabe von Exceptions von einem Modul A zu einem anderen Modul B zwar eine notwendige Voraussetzung, dass beide Module mit dem selben C++ Compiler übersetzt wurden und dass beide Module die selbe CRT-DLL benutzen - das alleine ist eben aber noch nicht ausreichend, weil es da noch mehr Fallstricke gibt!
Deine sämtlichen Beiträge hier sind total sinnlos, da du im Gegensatz zu mir keine Erfahrung mit dem Thema zu haben scheinst (und ich meine das konkrete Thema hier, also C++ Exceptions mit MSVC unter Windows!).
Was bringt es dem OP wenn du ihm schreibst "es könnte trotzdem Probleme" geben, wenn du ihm nicht konkret sagen kannst wieso/wodurch bzw. wie er diese Probleme beheben kann?Ich im Gegensatz dazu weiss dass es funktioniert. Ob es mit anderen Compiler Versionen auch funktioniert, oder unter anderen Betriebssystemen, und was GCC mit DWARF2 macht, ist alles uniniteressant.
Die Frage die noch bleibt, ist: wieso funktioniert es beim OP nicht?
-
Artchi schrieb:
hustbaer und Th69! Folgende Parameter beim Debug Config für alle Projekte: /EHsc /MDd
Das ist OK.
Ich nehme an es ist auch bei beiden Projekten die selbe Toolchain eingestellt ...?Artchi schrieb:
Vielleicht liegt es auch am Export? Alle meine Klassen haben folgendes Makro:
#ifdef CYAN_EXPORTS #define CYAN_API __declspec(dllexport) #else #define CYAN_API __declspec(dllimport) #endif class CYAN_API MyClass { ... };
Nö, völlig orthogonales Thema, völlig wurscht.
Das__declspec(dllimport)
brauchst du nichtmal wirklich. IIRC ist das nur in ein paar Sonderfällen interessant - z.B. wenn es darum geht ob inline Funktionen, WENN sie NICHT inline erweitert werden aus der DLL genommen oder im Programm nochmal neu angelegt werden.Artchi schrieb:
Wenn ich Exceptions werfe, dann die von std::exception geerbten. Ich habe auch auch schon ohne Erfolg versucht einfach plain int zu werfen, also
throw int(1);
Oder kann man nur eigene dllexportierte Klassen werfen? Wobei ja eigentlich die std-Klassen DLL-fähig sind?
Das ist alles egal, sollte alles gehen. Die Klasse muss genau genommen nichtmal exportiert werden, muss trotzdem funktionieren.
Artchi schrieb:
Oder hat die dllmain() etwas damit zu tun? Die sieht so aus (ist vom VC-Projekt autom. generiert worden): (...)
Nö, auch egal. Vor allem wenn die DllMain "leer" ist, so wie deine.
=>
Versuch mal ein neues, frisches EXE Projekt anzulegen und ein neues, frisches DLL Projekt, und dann mal den minimal nötigen Code schreiben um das zu testen. Also eine Funktion exportieren die z.B. nen std::runtime_error wirft, und die im Hauptprogramm in einem
try-catch(std::exception const&)
aufrufen.
-
hustbaer schrieb:
Deine sämtlichen Beiträge hier sinn total sinnlos
Deine Art gefällt mir überhaupt nicht! Ich schreibe hier in meiner Freizeit und ohne jegliche Bezahlung. Daher erwarte ich, dass das honoriert wird - völlig unabhängig davon, ob mein Beitrag nun sofort die perfekte Lösung liefern konnte oder nicht - und dass ich mich hier nicht noch beleidigen lassen muss. Falls irgendetwas, das ich geschrieben habe, technisch nicht 100% korrekt gewesen sein sollte (wovon ich allerdings nicht ausgehe), dann lasse ich mich gerne verbessern. Aber so gehört sich das nicht! Das ist die beste Methode, dass hier bald niemand versucht etwas beizutragen...
[quote="hustbaer"da du im Gegensatz zu mir keine Erfahrung mit dem Thema zu haben scheinst[/quote]
Mit dieser Behauptung liegst Du falsch.hustbaer schrieb:
Was bringt es dem OP wenn du ihm schreibst "es könnte trotzdem Probleme" geben, wenn du ihm nicht konkret sagen kannst wieso/wodurch bzw. wie er diese Probleme beheben kann?
Um ein Problem zu lösen, muss man zuerst einmal wissen, was das Problem ist. Oder sein könnte! Nicht wahr?
Und für manche Probleme gibt es nun mal nicht die einfache Lösung. Dann muss man sich vllt überlegen, was ein alternativer Weg sein könnte...
hustbaer schrieb:
Ich im Gegensatz dazu weiss dass es funktioniert.
Ich habe nie behauptet, dass die Übergabe von C++ Exceptions zwischen verschiedenen Modulen nicht funktionieren kann. Es müssen aber ganz bestimmte Voraussetzungen erfüllt sein (diese habe ich benannt) und es gibt diverse Fälle, in denen es eben doch nicht funktioniert (Beispiele habe ich genannt).
hustbaer schrieb:
Die Frage die noch bleibt, ist: wieso funktioniert es beim OP nicht?
Einige mögliche Ursachen, die der OP nun überprüfen kann/sollte, habe ich genannt. Das es evtl. doch ein anderes Problem sein könnte, dass noch nicht genannt wurde, habe ich nie ausgeschlossen...
-
DeathCubeUK schrieb:
hustbaer schrieb:
Deine sämtlichen Beiträge hier sinn total sinnlos
Deine Art gefällt mir überhaupt nicht!
Und die deine gefällt mir nicht. Du machst dich hier wichtig, indem du den OP und mich über Dinge belehrst die zumindest mir gut bekannt sind, aber vor allem: Dinge die bei der Fragestellung überhaupt nicht relevant sind.
Beispiel: Artchi hat von Anfang an klar gemacht, dass er alles mit dem selben Compiler übersetzt - und sogar dazugeschrieben um welchen Compiler es sich handelt. Und dann kommst du und schreibst darüber was es nicht alles für Probleme geben kann wenn man unterschiedliche Compiler verwendet. Wo macht das Sinn? Musst du mir bitte erklären.
DeathCubeUK schrieb:
Ich schreibe hier in meiner Freizeit und ohne jegliche Bezahlung.
Das ist keine Entschuldigung dafür anderen ihre Zeit zu stehlen.
Jedem kann mal ein Fehler passieren, man kann Dinge überlesen/übersehen, alles kein Problem. Daher hab' ich auch erst noch ganz sachlich/freundlich geantwortet.
Nur wenn das selbe unpassende Zeug dann wieder und wieder kommt, dann reichts halt irgendwann.DeathCubeUK schrieb:
hustbaer schrieb:
da du im Gegensatz zu mir keine Erfahrung mit dem Thema zu haben scheinst
Mit dieser Behauptung liegst Du falsch.
Naja, macht halt nicht den Eindruck.
DeathCubeUK schrieb:
(...)Einige mögliche Ursachen, die der OP nun überprüfen kann/sollte, habe ich genannt. Das es evtl. doch ein anderes Problem sein könnte, dass noch nicht genannt wurde, habe ich nie ausgeschlossen...
Bitte zitiere mir auch nur einen einzigen solchen Tip, der nicht durch Informationen die Artchi bereits davor gegeben hat irrelevant wird.
-
hustbaer schrieb:
Du machst dich hier wichtig, indem du den OP und mich über Dinge belehrst die zumindest mir gut bekannt sind, aber vor allem: Dinge die bei der Fragestellung überhaupt nicht relevant sind.
Wenn in einem Diskussions-Forum plötzlich Redebeiträge - wohlgemerkt Beiträge die sehr wohl für das diskutierte Problem relevant sind - als unerwünschte "Belehrungen" abgewatscht werden, dann verfehlt das ganze hier doch seinen Sinn! Was Du oder der OP schon wissen, das ist mir natürlich nicht bekannt. Ich kann nur Dinge erläutern, die meiner Meinung nach für die Analyse und Lösung des Problem wichtig sind, in der Hoffnung, dass es dem Thema weiter hilft!
Falls ein inhaltlich korrekter und in guter Absicht verfasster Post nicht zur sofortigen Lösung des Problems führen sollte, dann ist eine angemessene Antwort: "Danke, dass Du Dir die Zeit genommen hast, diese richtigen und wichtigen Punkte zu erläutern. Leider war mir das meiste davon schon bekannt und ich kann ausschließen, dass mein Problem auf einen dieser Punkte zurückzuführen ist." Damit ist die Sache erledigt und es kann weiter gehen. Eine angemessene Antwort ist sicher nicht "Deine sämtlichen Beiträge hier sind total sinnlos". In wie fern hilft das weiter? Wenn ich etwas "sinnlos" finde, dann derartiges Rumgestänker, wie es ständig aus Deiner Richtung kommt. Sprichst Du im "wahren Leben" etwa auch so mit Deinen Kollegen?
hustbaer schrieb:
Das ist keine Entschuldigung dafür anderen ihre Zeit zu stehlen.
hustbaer schrieb:
Jedem kann mal ein Fehler passieren, man kann Dinge überlesen/übersehen, alles kein Problem. Daher hab' ich auch erst noch ganz sachlich/freundlich geantwortet.
Nur wenn das selbe unpassende Zeug dann wieder und wieder kommt, dann reichts halt irgendwann.Ich stehe weiterhin dazu, dass alles, was ich geschrieben habe, korrekt war. Wenn davon irgend etwas technisch falsch (oder unvollständig) gewesen sein sollte, lasse ich mich gerne korrigieren. Aber ob nun die Punkte, die ich erklärt habe, für das Problem wichtig bzw. relevant sind, darüber mag man subjektiv unterschiedlicher Meinung sein. Ich bin der Meinung, dass ja. Du ganz offensichtlich nicht. So oder so, ist es kein angemessenes Verhalten, meine Beiträge als "sinnlos" abzutun. Selbst wenn Du der Meinung bist, mein Beträge hätten das Thema nicht weiter gebracht (eine Meinung die ich keineswegs teilen kann), dann solltest Du anerkennen, dass hier jemand in guter Absicht versucht zu helfen. Indem Du immer gleich drauf haust, wenn jemand mal - deiner Meinung nach - nicht die "perfekte" Antwort liefert, sorgst Du höchstens dafür, dass dass hier bald keiner mehr Lust verspürt, sich an der Diskussion zu beteiligen. Bei mir hast Du das übrigens geschafft.
DeathCubeUK schrieb:
hustbaer schrieb:
da du im Gegensatz zu mir keine Erfahrung mit dem Thema zu haben scheinst
Mit dieser Behauptung liegst Du falsch.
Naja, macht halt nicht den Eindruck.
DeathCubeUK schrieb:
(...)Einige mögliche Ursachen, die der OP nun überprüfen kann/sollte, habe ich genannt. Das es evtl. doch ein anderes Problem sein könnte, dass noch nicht genannt wurde, habe ich nie ausgeschlossen...
1. Kann der OP ausschließen, dass an irgend einer Stelle evtl. doch "fremd" DLL's im Spiel sind, die (wie ich erläutert habe) sehr wohl Probleme verursachen können, auch dann wenn sie selbst weder Quelle noch Ziel der Exception sind? Ich behaupte an dieser Stelle nicht, dass das das Problem ist, sondern weise lediglich darauf hin, dass man es im Hinterkopf behalten und ggf. überprüfen sollte.
2. Ich habe darauf hin gewiesen, dass es generell besser ist, C++ Exceptions abzufangen, bevor sie die DLL verlassen und stattdessen geeignete Fehlercodes zurück zugeben. Ich spreche dabei aus langjähriger Erfahrung in der Entwicklung diverser kommerzieller Projekte, auf verschiedenen Plattform (Windows, Linux, MacOS) und verschiedenen Compilern (MSVC, GCC/MingW, etc). Du kannst hier natürlich gerne anderer Meinung sein, solltest meine Meining - die ich für durchaus begründet halte - aber trotzdem respektieren!
(Dies wird übrigens auf absehbare Zeit mein letzter Beitrag hier sein. Ich bin es einfach Leid, mich für ein oder zwei Beiträge anschließend endlos rechtfertigen zu müssen)
-
DeathCubeUK schrieb:
Falls ein inhaltlich korrekter und in guter Absicht verfasster Post nicht zur sofortigen Lösung des Problems führen sollte, dann ist eine angemessene Antwort: "Danke, dass Du Dir die Zeit genommen hast, diese richtigen und wichtigen Punkte zu erläutern. Leider war mir das meiste davon schon bekannt und ich kann ausschließen, dass mein Problem auf einen dieser Punkte zurückzuführen ist." Damit ist die Sache erledigt und es kann weiter gehen.
Lies vielleicht nochmal meine erste Antwort an dich in diesem Beitrag:
https://www.c-plusplus.net/forum/p2441806#2441806
Da war bitteschön nichts unfreundliches dran. Ich habe bestätigt dass einige deiner Aussagen korrekt sind bzw. (wörtlich) "zum Teil richtig" sind (inklusive sachlicher Erklärung warum ich es nur für "zum Teil richtig" halte).
Weiters die sachliche Klarstellung dass dein Beitrag auf das Problem des OP nicht anwendbar ist.Nach deiner Aussage könnte es jetzt also "weiter gehen". Tut es aber nicht, denn dann kommt...
https://www.c-plusplus.net/forum/p2441814#2441814
Eine weitere Belehrung über immer noch (auf die Fragestellung bezogen) total irrelevante Dinge.Und dann fängt es halt an lästig zu werden, weil sich ein gewisses Bild abzeichnet.
DeathCubeUK schrieb:
Dies wird übrigens auf absehbare Zeit mein letzter Beitrag hier sein.
OK. Wenn du meinst.
DeathCubeUK schrieb:
Ich bin es einfach Leid, mich für ein oder zwei Beiträge anschließend endlos rechtfertigen zu müssen
Die alternative Lösung dafür wäre: schreib Beiträge die besser zum Thema passen.
-
DeathCubeUK schrieb:
Ich bin es einfach Leid, mich für ein oder zwei Beiträge anschließend endlos rechtfertigen zu müssen
Musst du nicht. Tue es nicht.
-
hustbaer schrieb:
Versuch mal ein neues, frisches EXE Projekt anzulegen und ein neues, frisches DLL Projekt, und dann mal den minimal nötigen Code schreiben um das zu testen. Also eine Funktion exportieren die z.B. nen std::runtime_error wirft, und die im Hauptprogramm in einem
try-catch(std::exception const&)
aufrufen.Ich habe jetzt mal eine komplett neue Solution mit minimalen Projekt-Inhalten erstellt. Tatsächlich funktioniert hier, dass die Exception die DLL verlässt.
Das habe ich in meinem ursprünglichen Projekt anders:
Ich benutze dort die ATL (sind nur Termplates), d2d1.lib (Direct2D), dwrite (DirectWrite) und Windowscodecs.lib.
Sonst fällt mir nicht weiter ein, was ich ggü. dem Dummy-Projekt anders habe.
Kann es an den D2D Libs liegen?
-
Weil du die ATL erwähnst: ist hier evtl. COM im Spiel?
Falls ja könnte ich mir evtl. vorstellen dass COM da irgendwie dazwischenpfuscht - vielleicht ein automatisch erstellter Proxy oder sowas.Nur die Verwendung der ATL ist auf jeden Fall kein Problem. Ich habe hier auch ein Projekt wo in der .EXE die ATL verwendet wird, wo die .EXE etliche (nicht-COM) DLLs nachlädt, die Fehler mittels Exceptions kommunizieren. Funktioniert alles wie es sollte.
Was D2D angeht: damit hab ich keine Erfahrung. Würde mich aber wundern wenn das irgendwie relevant wäre.
Nochwas: du schreibst die Exception "geht verloren". Was passiert denn statt der erwarteten Ausfürhung des catch-Blocks?
Läuft die Funktion nach der "throw" Anweisung weiter? Oder geht's an der DLL-Boundary weiter als ob die Funktion normal beendet worden wäre? Oder ...?Ansonsten kann ich dir nur noch empfehlen: bau Stück für Stück die Dinge die in dem Projekt drinnen sind wo es nicht funktioniert aus, und teste nach jedem Schritt ob es immer noch Probleme macht. (Oder auch umgekehrt: bau Stück für Stück die anderen Dinge ein, ausgehend von dem Projekt wo es jetzt funktioniert.)
-
Liegen Aufrufe von Fensterprozeduren auf dem Stack?
COM Aufrufe?
-
Martin Richter schrieb:
Liegen Aufrufe von Fensterprozeduren auf dem Stack?
COM Aufrufe?Ich verwende die ATL um Fenster mittels CWindow zu erzeugen und die Messages zu behandeln. Ob ich die CWindow-Objekte auf dem Stack erzeugt habe, muss ich heute Abend im Code nachschauen.
Bei den COM-Aufrufen, dürften ja die ganzen DirectXYZ-APIs dazu gehören?
-
Wenn bestimmte Exceptions innerhalb von SendMessage und einer Fenster Prozedur auftreten, dann werden die auch "gefressen". Das kann ich auch immer wieder beobachten.
-
hustbaer schrieb:
Nochwas: du schreibst die Exception "geht verloren". Was passiert denn statt der erwarteten Ausfürhung des catch-Blocks?
Läuft die Funktion nach der "throw" Anweisung weiter? Oder geht's an der DLL-Boundary weiter als ob die Funktion normal beendet worden wäre? Oder ...?Ich werfe in der DLL eine Exception, z.B.
throw int(1);
. Im Debugger kann ich dann auch das Stack-Unwinding mitverfolgen (z.B. weil das Throw etwas tiefer in der DLL passiert). Es kommt auch ein First-Chance-Exception auf dem Trace-Output. Das Unwindig hört aber an der ersten DLL-Funktion auf. Es passiert nichts weiter bzw. es geht dann normal mit dem Programmablauf weiter. Und in der EXE ist als ob nichts gewesen ist, obwohl ich in der EXE die DLL-Funktion (die den throw wirft) aufgerufen habe.Normalerweise müsste ein Unhandled Exception kommen. Wenn ich in der Exe ein try Catch einbaue, gehts auch nie den Catch Block.
-
Martin Richter schrieb:
Wenn bestimmte Exceptions innerhalb von SendMessage und einer Fenster Prozedur auftreten, dann werden die auch "gefressen". Das kann ich auch immer wieder beobachten.
Mein erste Versuch eine Exception zu werfen, war bei einem WM_PAINT. Meine erste Vermutung war, das die ATL Messages vielleicht einen catch-Block haben o.ä. Konnte in den ATL Sourcen nichts finden.
Deshalb hatte ich als Test auch probiert, eine Exception zu werfen, die nichts mit ATL zu tun hat:
// Dummy Code in der EXE int WinMain() { MyAtlWindow w; // DLL w.show(); MyPlainClass c; // DLL c.foo(); // hier drin wird eine exception geworfen! // First-Chance Exception, aber kein Unhandled Exception while() { aufwändiger windows message loop kram }; }
Da es auch so nichts bewirkt hat, hatte ich ATL bzw. CWindow-Messages ausgeschlossen.
Ich könnte heute Abend probieren in meiner Solution die CWindow Erzeugung weg zu lassen. Und dann al schauen ob es funktioniert. Ich ahne es aber schon, das wahrscheinlich das ganze Window-Handling in der DLL daran schuld ist...
-
Das Exceptions in einem WM_PAINT Block "gefressen" werden ist ein bekanntes Problem, das zum Teil auch noch durch Bugs verschärft wird. (Siehe auch http://support.microsoft.com/kb/976038).
Siehe auch Diskussion hier:
http://stackoverflow.com/questions/1487950/access-violation-in-wm-paint-not-caughtAber dennoch ist mir unklar, was bei Dir passiert mit der "plain class". Irgendwie leuchtet mir das nicht ein und mir scheint ein weiterer Exception Handler zu existieren.
Was sagt SetUnhandledExceptionFilter wenn Du es explizit auf NULL setzt? Ist einer gesetzt gewesen? Was passiert nun, wenn er nicht NULL war?
-
@Martin Richter
Weisst du ob diese "exceptions in callbacks werden gefressen" Geschichte mit SP1 behoben wurde (gibt ja nen Hotfix)?
EDIT: OK, never mind, hab's selbst grad gefunden: ja, ist drinnen. => Sollte heute also kein Problem mehr sein.