WindowsDLL
Jump to navigation
Jump to search
This page contains German notes about the DLL mechanics on Windows.
TODO: Translate and beautify this page.
Basics
- http://msdn.microsoft.com/en-us/library/1ez7dh12.aspx
- Im Gegensatz zu Applikationen, von denen mehrere Instanzen laufen kˆnnen, werden DLLs nur ein einziges Mal instanziert
- Suchpfad, der vom System verwendet wird, um eine DLL zu finden: http://msdn.microsoft.com/en-us/library/7d83bc18.aspx
- Laden und Freigeben von DLLs
- http://msdn.microsoft.com/en-us/library/zzk20sxw.aspx (LoadLibrary)
- http://msdn.microsoft.com/en-us/library/h4ff11tc.aspx (FreeLibrary)
Type of DLLs
- http://msdn.microsoft.com/en-us/library/9se914de.aspx
- Win32 DLL
- Dies ist ein allgemeiner Begriff f¸r DLLs auf der Win32 Plattform
- Ich habe den Begriff auch schon als Synonym zu "Non-MFC DLL" gesehen
- Non-MFC DLL
- http://msdn.microsoft.com/en-us/library/3x2ta076.aspx
- Benutzt kein MFC und linkt auch nicht gegen MFC
- Regular DLL (aka "MFC DLL" oder "Win32 DLL")
- Benutzt MFC und linkt entweder gegen die statische oder die dynamische Version von MFC
- Eine Regular DLL kann sowohl von MFC als auch von Non-MFC Applikationen benutzt werden
- Eine Regular DLL (egal ob statisch oder dynamisch gegen MFC gelinkt) muss ein CWinApp Objekt erzeugen
- InitInstance() und ExitInstance() Methoden des Objekts werden automatisch beim Laden der DLL augerufen (von der MFC-Version von DllMain())
- Das Objekt hat jedoch keinen Main Message Pump, deswegen treffen auch die sonst ¸blichen Dinge in Bezug auf CWinApp::Run() nicht zu
- Statisches Linken gegen MFC
- http://msdn.microsoft.com/en-us/library/f22wcbea.aspx
- Muss global das folgende Preprocessor Makro verwenden:
- _USRDLL
- _foo_ (steht nur hier, um WikidPad dazu zu bringen, den folgenden Text nicht kursiv darzustellen)
- Dieser Typ von DLL wurde fr¸her als "USRDLL" bezeichnet
- Weitere Details: http://msdn.microsoft.com/en-us/library/f22wcbea.aspx
- Dynamisches Linken gegen MFC
- http://msdn.microsoft.com/en-us/library/30c674tx.aspx
- Muss global die folgenden Preprocessor Makros verwenden:
- _USRDLL
- _AFXDLL
- _foo_ (steht nur hier, um WikidPad dazu zu bringen, den folgenden Text nicht kursiv darzustellen)
- Muss folgendes Code-Schnipsel am Anfang jeder exportierten Funktion (bzw. Funktion, die von aussen aufgerufen werden kann) aufrufen
AFX_MANAGE_STATE(AfxGetStaticModuleState())
- AFX_MANAGE_STATE sollte *nicht* verwendet werden f¸r Regular DLLs, die statisch gegen MFC linken
- Weitere Details: http://msdn.microsoft.com/en-us/library/30c674tx.aspx
- Speicher, der in einer Regular DLL alloziert wird, sollte in der DLL bleiben, insbesondere sollte folgendes *nicht* gemacht werden:
- Keine MFC-Objekte mit dem aufrufenden Executable austauschen
- Keine Pointers auf Speicher, der von MFC angelegt wurde, mit dem aufrufenden Executable austauschen
- Falls dies doch gemacht werden muss, so muss die DLL eine MFC Extension DLL sein
- TODO: Zur Zeit nicht klar, was die Folgen sind, wenn man die Regel nicht befolgt
- TODO: Zur Zeit nciht ganz klar, was "MFC Objekt" bzw. "von MFC angelegter Speicher" ist
- MFC Extension DLL
- http://msdn.microsoft.com/en-us/library/20ytt2wa.aspx
- Eine MFC Extension DLL kann nur von MFC Applikationen benutzt werden
- Diese Art von DLL muss verwendet werden...
- If your DLL implements reusable classes derived from the existing MFC classes
- Or you need to pass MFC-derived objects between the application and the DLL
- Es ist mir nicht klar, was damit gemeint ist - schliesslich verwenden wir die ganze Zeit CDialog usw.
- Um eine MFC Extension DLL zu erzeugen m¸ssen die folgenden Makros projektweit definiert sein
- _AFXDLL
- _AFXEXT
- _foo_ (steht nur hier, um WikidPad dazu zu bringen, den folgenden Text nicht kursiv darzustellen)
- Dieser Typ von DLL wurde fr¸her als "AFXDLL" bezeichnet
- Resource-Only DLL
- http://msdn.microsoft.com/en-us/library/24b2tcy0.aspx
- Enth‰lt nur Ressourcen aber keinen Code
- Muss die Linker-Option "/NOENTRY" verwendet
- Um die DLL zu verwenden muss sie mit LoadLibrary() geladen werden
- USRDLL und AFXDLL
- Diese Begriffe sind historisch, sie bezeichnen DLL-Typen in Visual C++ 3.x und fr¸her
- USRDLL heute = Regular DLL, die statisch gegen MFC linkt
- AFXDLL heute = MFC Extension DLL
- Redistribution von MFC
- Linkt eine DLL dynamisch gegen MFC so muss man die DLL zusammen mit der passenden Version der MFC DLLs ausliefern
Export symbols from a DLL
- http://msdn.microsoft.com/en-us/library/z4zxe9k8.aspx
- Exports Table
- Funktionen, die von einer DLL exportiert werden, sind in der "Exports Table" der DLL aufgelistet
- Die Tabelle kann mit "dumpbin /exports" angesehen werden
- Funktionen in der Tabelle werden als *Entry Point* bezeichnet
- Funktionen, die nicht in der Exports Table auftauchen, gelten als "private"
- Import Library
- .lib = Import Library File
- Im Gegensatz zur Exports Table, die direkt in der DLL selbst steht, kann beim Builden der DLL eine sogenannte Import Library generiert werden
- Die Import Library enth‰lt alle von der DLL exportierten Symbole
- Die Import Library wird verwendet, wenn man gegen die DLL linken will - man *importiert* also quasi die von der DLL exportierten Symbole
- Exportieren mit .def File
- .def = Module Definition File
- Im wesentlichen verwendet man dieses File, um Funktionen ¸ber eine "Ordinal", eine Art Index-Zahl, zu exportieren
- C++ Funktionen m¸ssen mit ihrem "decorated name" aufgef¸hrt werden
- Details siehe http://msdn.microsoft.com/en-us/library/d91k01sh.aspx
- Exportieren mit __declspec(dllexport)
- Wird dieses Schl¸sselwort bei der Deklaration eines Symbols verwendet, so wird das Symbol exportiert, ohne dass man es im .def File auff¸hren muss
- Besonders interessant, da man keine C++ Name Decoration im .def File zusammenbasteln muss
- Exportieren mit AFX_EXT_CLASS
- MFC Extension DLLs benutzen AFX_EXT_CLASS, um ganze Klassen oder einzelne Funktionen zu exportieren
- Das Makro expandiert zu declspec(dllexport), wenn die korrekten Makros f¸r eine MFC Extension DLL gesetzt sind
- Details siehe http://msdn.microsoft.com/en-us/library/9xyb5w93.aspx
- Importieren mit __declspec(dllimport)
- Will man gegen eine DLL linken, so benˆtigt man die Import Library der DLL
- Zus‰tzlich benˆtigt man die Header Files des Source Codes
- In den Header Files muss bei allen exportierten Symbolen declspec(dllimport) stehen
Initialising a DLL
- http://msdn.microsoft.com/en-us/library/988ye33t.aspx (Run-Time Library Behavior)
- http://msdn.microsoft.com/en-us/library/7h0a8139.aspx
- Wenn eine DLL geladen wird, so kann spezieller Initialisierungs- und Terminierungs/Cleanup-Code ausgef¸hrt werden
- Der Typ der DLL entscheidet, wo dieser Code stehen muss
- Non-MFC DLL = Funktion DllMain()
- Regular DLL = CWinApp Klasse, Methoden InitInstance() und ExitInstance()
- MFC Extension DLL = Funktion DllMain(), die vom "MFC DLL Wizard" (was auch immer das genau ist) generiert wird
- In allen DLL Typen kann optional eine DllMain() Funktion geschrieben werden
- Bei Regular DLLs sollte man allerdigns DllMain() nicht selber codieren, da MFC eine eigene Version der Funktion hat, die InitInstance() etc. aufruft
- DllMain() wird sowohl beim Initailisieren als auch beim Terminieren aufgerufen wird
- Der Grund f¸r den Aufruf ist anhand eines Funktionsparameters erkennbar
Module State (AFX_MANAGE_STATE)
- Referenzen
- http://msdn.microsoft.com/en-us/library/w25be484.aspx (Module States of a Regular DLL Dynamically Linked to MFC)
- http://msdn.microsoft.com/en-us/library/0asx94f7.aspx (Managing the State Data of MFC Modules)
- http://msdn.microsoft.com/en-us/library/ba9d5yh5.aspx (AFX_MANAGE_STATE)
- http://msdn.microsoft.com/en-us/library/ft1t4bbc.aspx (TN058: MFC Module State Implementation)
- Konzeptionell beinhaltet MFC ein Set von globalen Daten wie z.B.
- Pointer auf das aktuelle CWinApp Objekt
- Handle maps
- Dieses Konzept wirkt sich auf das MFC API aus
- AfxGetApp() gibt z.B. einen Pointer auf "das" CWinApp Objekt zur¸ck
- Es gibt keine Mˆglichkeit, um anzugeben, welche CWinApp Instanz gew¸nscht ist
- AfxGetApp() nimmt stillschweigend an, dass es nur ein globales CWinApp Objekt geben kann
- Das Konzept ist historisch bedingt und geht davon aus, dass es diese globale Daten nur einmal pro Applikation gibt
- Fr¸her konnte man nur statisch gegen MFC linken, weshalb das Konzept ok war
- Heute dagegen kann man eine DLL dynamisch gegen MFC linken, was dazu f¸hrt, dass das Konzept nicht mehr sauber funktioniert
- Details zum Module State (aus TN058)
- Jeder Thread Context enth‰lt einen Pointer auf den aktuellen Module State
- Der Pointer kann ge‰ndert werden mit Hilfe von AfxSetModuleState()
- Der Pointer kann (vermutlich) abgefragt werden mit AfxGetModuleState()
- Gem‰ss TN058 ‰ndert sich der Pointer auf den aktuellen Module State automatisch, wenn beim Ausf¸hren von Code von einem Module in ein anderes gewechselt wird ("when the thread of execution passes a module boundary")
- Es ist nicht ganz klar, was "automatisch" hier bedeutet; nachfolgend jedoch eine starke Vermutung
- Irgendwo in MFC ist der Module State als globale Variable hinterlegt
- Beim Linken gegen die statische MFC "erh‰lt" die linkende DLL eine private Kopie der Variable
- Wird nun in die DLL gesprungen, so wird diese private Kopie verwendet, wenn auf das Symbol der Variable zugegriffen wird
- Beim Linken gegen die dynamische MFC "erh‰lt" die linkende DLL eine Referenz auf die externe globale Variable
- Wird nun in die DLL gesprungen, so wird die globale Variable verwendet, die in der dynamischen MFC abgelegt ist
- Ruft man AFX_MANAGE_STATE auf so ver‰ndert man die globale Variable auch f¸r alle anderen DLLs, die gegen die dynamische MFC gelinkt sind
- TN058 erw‰hnt das Makro AFX_MANAGE_STATE
- http://msdn.microsoft.com/en-us/library/ba9d5yh5.aspx
- Das Makro funktioniert wie ein Stack: Beim Ausf¸hren wird der Module State gepushed, beim Verlassen der Funktion wird er gepopped
- Dazu wird vermutlich AfxSetModuleState() verwendet
- Mehrmaliges Pushen des gleichen Module States ist kein Problem
- TN058 erw‰hnt ebenfalls die Funktion AfxGetStaticModuleState()
- http://msdn.microsoft.com/en-us/library/cc6feexs.aspx
- Die Funktion gibt einen Pointer zur¸ck, der auf den Module State zeigt, der f¸r das "aktuelle Module" g¸ltig ist
- Das "aktuelle Module" ist dasjenige, aus dem gerade im Moment Code ausgef¸hrt wird
- Vermutlich wird dieses Problem ¸ber eine statische Struktur gelˆst, die beim Laden der DLL initialisiert wird
- Fazit: Damit MFC weiterhin so funktionieren kann, wie es urspr¸nglich angedacht war, wird folgendes empfohlen
- In jeder Funktion, die von einer *dynamisch gegen MFC gelinkten Regular DLL* exportiert wird, soll folgendes Code-Schnipsel am Anfang stehen:
AFX_MANAGE_STATE(AfxGetStaticModuleState())
- Tats‰chlich sind nicht nur mit declspec(dllexport) exportierte Funktionen betroffen, sondern jeder andere "Entry Point" in eine DLL
- Z.B. also die Funktionen einer COM Schnittstelle
- *Nicht betroffen* von dem ganzen State Switching sind
- Statisch gelinkte Regular DLLs: Diese Module verwenden nicht das von der DLL-Version von MFC exportierte Symbol f¸r den globalen Module State, sondern enthalten wegen dem statischen Linken eine private Kopie davon. MFC Funktionsaufrufe gehen nicht in die MFC DLL, sondern bleiben innerhalb des DLL.
- MFC Extension DLLs: Bei diesen ist die Idee, dass sie den Module State der Applikation verwenden
- Inhalt des Module State
- Der Module State ist in einer Struktur vom Type AFX_MODULE_STATE abgelegt
- m_pCurrentWinApp = Pointer auf CWinApp Objekt, welches von AfxGetApp() zur¸ckgegeben wird
- m_hCurrentInstanceHandle = Module Handle, welches von AfxGetInstanceHandle() zur¸ckgegeben wird
- m_hCurrentResourceHandle = Ressource Module Handle, welches von AfxGetResourceHandle() zur¸ckgegeben wird
- m_lpszCurrentAppName
- etc.
- Praktische Anwendung von AFX_MANAGE_STATE
- Verwenden, falls Ressourcen geladen werden m¸ssen
- Es sollte auf jeden Fall das Ressource Handle verwendet werden
- Hat man sich das Ressource Handle einmal am Anfang gemerkt, kann man sogar ohne AFX_MANAGE_STATE auskommen
- Verwenden, falls Windows und Dialoge erstellt werden
- Beim Erstellen von Windows werden Handles (HWND) im Module State hinterlegt; evt. sogar noch mehr
- In einigen F‰llen muss AFX_MANAGE_STATE nicht verwendet werden, da MFC das bereits f¸r uns tut. Bekannte Beispiele
- CWinApp::InitInstance()
- Message Map Handlers
- Verwenden, falls Ressourcen geladen werden m¸ssen
- Details zu AFX_MANAGE_STATE (gesehen w‰hrend Debugging)
- AFX_MANAGE_STATE ist ein Makro, das eine lokale Variable vom Type AFX_MAINTAIN_STATE2 deklariert
- AFX_MAINTAIN_STATE2 ist eine struct, die in afxstat.h deklariert ist
- Constructor AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2() ist implementiert in afxstate.cpp
- Destructor AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2() ist implementiert in afxwin1.inl
- Im Constructor des AFX_MAINTAIN_STATE2 Objekts wird der aktuelle globale Module State gesetzt
- Beim Verlassen des aktuellen Kontexts wird das AFX_MAINTAIN_STATE2 Objekt automatisch zerstˆrt
- Im Destructor wird der vorhergehende globale Module State wiederhergestellt
- Neben dem Module State wird auch noch der sogenannte "Activation Context" (siehe SideBySide) gemanaged
- Aber nur, falls AfxGetAmbientActCtx() == true
- AFX_MANAGE_STATE ist ein Makro, das eine lokale Variable vom Type AFX_MAINTAIN_STATE2 deklariert