44 Stimmen

Was geschieht mit globalen Variablen, die in einer DLL deklariert sind?

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?

45voto

paercebal Punkte 78198

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:

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.

8voto

Mark Ransom Punkte 283960

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

5voto

MSN Punkte 51308

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.

3voto

Philip Rieck Punkte 31977

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.

2voto

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

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