Angenommen, ich schreibe eine DLL in C++ und deklariere ein globales Objekt einer Klasse mit einem nicht-trivialen Destruktor. Wird der Destruktor aufgerufen, wenn die DLL entladen wird?
Antworten
Zu viele Anzeigen?In einer Windows C++-DLL werden alle globalen Objekte (einschließlich der statischen Mitglieder von Klassen) kurz vor dem Aufruf der DllMain mit DLL_PROCESS_ATTACH erstellt und kurz nach dem Aufruf der DllMain mit DLL_PROCESS_DETACH zerstört.
Nun müssen Sie drei Probleme berücksichtigen:
0 - Natürlich sind globale Nicht-Konstanten-Objekte böse (aber das wissen Sie ja schon, also werde ich es vermeiden, Multithreading, Sperren, Gott-Objekte usw. zu erwähnen).
1 - Die Reihenfolge der Erstellung von Objekten oder verschiedenen Kompiliereinheiten (d.h. CPP-Dateien) ist nicht garantiert, so dass man nicht darauf hoffen kann, dass das Objekt A vor B erstellt wird, wenn die beiden Objekte in zwei verschiedenen CPPs instanziiert sind. Dies ist wichtig, wenn B von A abhängt. Die Lösung besteht darin, alle globalen Objekte in dieselbe CPP-Datei zu verschieben, da innerhalb derselben Kompilierungseinheit die Reihenfolge der Instanziierung der Objekte die Reihenfolge der Konstruktion ist (und die Umkehrung der Reihenfolge der Zerstörung)
2 - Es gibt Dinge, die in der DllMain verboten sind. Diese Dinge sind wahrscheinlich auch in den Konstruktoren verboten. Vermeiden Sie also, etwas zu sperren. Siehe Raymond Chen's ausgezeichneten Blog zu diesem Thema:
- Einige Gründe, nichts Unheimliches in Ihrer DllMain zu tun
- Ein weiterer Grund, in der DllMain nichts Unheimliches zu tun: Unbeabsichtigtes Deadlock
- Einige Gründe, nichts Unheimliches in Ihrer DllMain zu tun, Teil 3
In diesem Fall könnte die faule Initialisierung interessant sein: Die Klassen bleiben in einem "uninitialisierten" Zustand (interne Zeiger sind NULL, Boolesche Werte sind falsch, was auch immer), bis man eine ihrer Methoden aufruft, woraufhin sie sich selbst initialisieren. Wenn Sie diese Objekte innerhalb der Hauptfunktion (oder einer der Nachfolgefunktionen der Hauptfunktion) verwenden, ist alles in Ordnung, da sie nach der Ausführung von DllMain aufgerufen werden.
3 - Wenn einige globale Objekte in DLL A von globalen Objekten in DLL B abhängen, sollten Sie natürlich sehr vorsichtig sein, was die Reihenfolge des Ladens der DLL und damit die Abhängigkeiten angeht. In diesem Fall werden Ihnen DLLs mit direkten oder indirekten zirkulären Abhängigkeiten wahnsinnig viel Kopfzerbrechen bereiten. Die beste Lösung ist, die zirkulären Abhängigkeiten aufzulösen.
P.S.: Beachten Sie, dass in C++, Konstruktor werfen kann, und Sie wollen nicht eine Ausnahme in der Mitte eines DLL-Laden, so sicher sein, dass Ihre globalen Objekte werden nicht mit Ausnahme ohne einen sehr, sehr guten Grund sein. Da korrekt geschriebene Destruktoren nicht berechtigt sind, eine Exception auszulösen, sollte das Entladen der DLL in diesem Fall in Ordnung sein.
Diese Seite von Microsoft geht auf die Einzelheiten der DLL-Initialisierung und der Zerstörung von Globals ein:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx
Wenn Sie den tatsächlichen Code sehen wollen, der beim Verknüpfen einer .dll ausgeführt wird, sehen Sie sich %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c
.
Ab der Inspektion werden die Destruktoren über _cexit()
wenn der interne Referenzzähler, der vom dll CRT verwaltet wird, den Wert Null erreicht.
Sie sollte aufgerufen werden, wenn entweder die Anwendung beendet oder die DLL entladen wird, je nachdem, was zuerst eintritt. Beachten Sie, dass dies ein wenig von der tatsächlichen Laufzeit abhängt, gegen die Sie kompilieren.
Außerdem sollte man sich vor nicht-trivialen Destruktoren in Acht nehmen, da es sowohl Zeit- als auch Ordnungsprobleme gibt. Ihre DLL kann entladen werden nach eine DLL, auf die sich Ihr Destruktor verlässt, was natürlich Probleme verursachen würde.
Unter Windows sind binäre Bilddateien mit der Erweiterung *.exe, *.dll in PE-Format Solche Dateien haben Entry Point. Sie können sie mit einem Dumpbin-Tool wie
dumpbin /headers dllname.dll
Wenn Sie die C-Laufzeitumgebung von Microsoft verwenden, dann ist Ihr entr *CRTStartup oder *DllMainCRTStartup
Solche Funktionen führen die Initialisierung der C- und C++-Laufzeit durch und delegieren die Ausführung an (main, WinMain) bzw. an DllMain.
Wenn Sie Microsofts VC-Compiler verwenden, können Sie sich den Quellcode dieser Funktionen in Ihrem VC-Verzeichnis ansehen:
- crt0.c
- dllcrt0.c
DllMainCRTStartup Prozess alle Dinge müssen init/deinit Ihre globalen Variablen aus .data Abschnitte im normalen Szenario, wenn es retrive Benachrichtigung DLL_PROCESS_DETACH während dll unload. Zum Beispiel:
- main oder WinMain des Start-Threads des Programms gibt den Kontrollfluss zurück
- Sie rufen explizit FreeLibrary auf und use-dll-counter ist Null
- See previous answers
- Weitere Antworten anzeigen