Tach,
da ich grade ein bisschen Assembler mache, dachte ich mir, dass es vielleicht jemanden interessiert. Bücher & Hefte raus, jetzt wird gelernt
Dynamic Link Libraries
Dabei handelt es sich um eine unter Windows verwendete Programmbibliothek. Sie haben das gleiche Format wie .exe - Dateien, enthalten also auch Opcodes, Ressourcen usw., enden aber auf .dll .
Warum?
Warum soll ich meinen Programm-Code in einer DLL verpacken, wenn ich es auch genausogut in die eigentliche Anwendung schreiben kann?
Ganz einfach: Ich bin vielleicht nicht der einzige, der den Kram in der DLL brauch! Vielleicht wollen andere Anwendungen auch gerne darauf zugreifen, aber das geht wohl schlecht, wenn es in meiner .exe eingebunden ist. Das führt natürlich auch zu ziemlicher Arbeits-Speicher-Schon-Tour: Die Datei muss nur einmal in den Speicher geladen werden und kann von da so oft wie nötig benutzt werden.
Es gibt sogar noch einen Vorteil, die sog. "Modularität":
Sagen wir, in der DLL seien irgendwelche Funktionen archiviert. Nun müssen aber einige dieser Funktionen geändert / geupdatet werden, wieauchimmer. Anstatt jetzt in allen Anwendungen diese Funktionen ändern zu müssen, nimmt man die Änderungen einmal an der DLL vor und alle Programme sind geupdatet. Im großen Stil wie z.B. beim Office Paket o.Ä. führt das natürlich zu enormen Arbeits-Vereinfachung.
DLLs werden auch gerne in noch einer anderen Fuktion verwendet: Als Plug-Ins.
Ich möchte gerne eine kleine Erweiterung zu meinem Lieblings-Programm schreiben. Eine koch-mir-den-kaffee-funktion. Kein Problem:
Den Code in einer DLL verfassen, Plug-In im Programm aktivieren, fertig ist. Dynamische Einbindbarkeit, Dynamic Linking
Wie?
Schön, schön, wie sieht das Ganze in der Praxis aus?
Nun, Windows bietet zwei Möglichkeiten, eine DLL einzubinden:
Implizit:
Windows lädt die DLL automatisch mit dem Client-Programm (das Programm, das die DLL "anfordert"), bevor der Client ausgeführt wird, sodass er auf die DLL zugreifen kann.
Diese Methode ist ratsam, wenn der Client mindestens eine Prozedur aus der DLL braucht, da Windows es ja sowieso in den Speicher lädt, da wir es Windows ja implizit mitgeteilt haben.
Explizit
Was passiert aber, wenn die DLL-Datei gar nicht da ist? Die Anwendung wird gar nicht erst gestartet werden. Das lässt sich mit explizitem Einbinden vermeiden:
Hierbei lädt Windows die DLL erst, wenn der erste Client die DLL
während des Ausführens "anfordert".
Zum Anfordern verwendet man (unter Assembly) folgende Funktionen:
LoadLibrary - um die DLL in den Speicher zu laden
GetProcAddress - um die Adresse der jeweiligen Funktionen zu erlangen
FreeLibrary - um die DLL wieder aus dem Speicher "freizulassen", weil sie nicht mehr gebraucht wird
Wie genau?
Nun, DLLs enthalten exportierte & nicht-exportierte Funktionen, sprich publike und private Funktionen. Die exportierten Funktionen dienen dem Client, wohingegen dieser von den privaten nichts weiß, diese dienen der DLL intern.
Jede DLL hat einen
Einsprungspunkt (Entrypoint). Dieser wird vom System "angesprochen", wenn ein Client nach der DLL verlangt. Im Einsprungspunkt kann die DLL auch Speicherplatz reservieren.
Ein typischer Einsprungspunkt ist so aufgebaut:
Code:
BOOL WINAPI DllEntryPoint(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
);
Es werden also beim Aufruf des Einsprungpunktes drei Parameter weitergegeben, von denen der dritte aber nicht benutzt wird.
hInstDLL ist der
module handle der DLL. Eine Art Erkennungsmarke der DLL, die sich nicht ändert, es sei denn die DLL wird neu-geladen.
fdwReason kann vier verschiedene Werte haben:
- DLL_PROCESS_ATTACH - das nutzt man, wenn die DLL zum ersten Mal in den Speicher gelesen wird. Es dient zur Initialisierung.
- DLL_PROCESS_DETACH - hiermit wird die DLL aus dem Speicher gelesen. Man kann zB. Speicher wieder freigeben.
- DLL_THREAD_ATTACH - man erzeugt einen neuen Thread.
- DLL_THREAD_DETACH - ein Thread im Prozess wird wieder gelöscht.
Funktionen in der DLL können irgendwo abgelagert werden, aber wenn sie exportiert werden sollen, dann müssen sie zusätzlich in einer .DEF-Datei verzeichnet sein. Dazu später.
Das DLL-Skelett
*Bemerkung: Ich verwende MASM-Syntax & -Bibliotheken
DLLSkelett.asm
Code:
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
.data?
.code
DLLEntry proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
.if reason == DLL_PROCESS_ATTACH ; Initialisierung,wenn die DLL geladen wird
mov eax,TRUE ; eax muss TRUE sein, damit die DLL läuft
.elseif reason == DLL_PROCESS_DETACH
; Code für das Entladen der DLL
.elseif reason == DLL_THREAD_ATTACH
; Thread im Prozess starten
.elseif reason == DLL_THREAD_DETACH
; Thread im Prozess zerstören
.endif
Ret
DLLEntry Endp
ExpFunc1 proc
; Code für die erste Funktion
Ret
ExpFunc1 EndP
ExpFunc2 proc
; Code für die zweite Funktion
Ret
ExpFunc2 EndP
ExpFunc3 proc
; Code für die dritte Funktion
Ret
ExpFunc3 EndP
end DLLEntry
Skelett.def
Code:
LIBRARY DLLSkelett
EXPORTS _ExpFunc1 @ 01
_ExpFunc2 @ 02
_ExpFunc3 @ 03
Hehehe, ich halte den Programmcode für ziemlich selbst-erklärend. Später mehr dazu, wie man die Funktionen dann auch aufrufen kann.
*P.S.:
Für Code-Beispiele in Hochsprachen siehe Wikipedia:
Dynamic Link Library - Wikipedia