Wenn man eine
DLL erstellen möchte, dann steht man unter Anderem vor einer "schwierigen" Entscheidung:
"Wo um Himmels Willen soll ich meine DLL im Speicher ablegen?", oder so ähnlich.
Denn vollkommen willkürlich sollte/kann man diese nicht positionieren - vorallem nicht an der Standardadresse $00400000, denn dort liegt meistens schon die EXE rum.
Und dann wuseln bestimmt auch noch andere
DLL's im selben Adressraum herum, welche entweder schon den gewünschten Bereich belegen, oder welche dann ebenfalls verschoben werden müssen, weil sich die eigene
DLL dort breit gemacht hat.
Wenn man dann auch noch mehrere
DLL's erstellt, welche gleichzeitig in der selben EXE integriert sind, wird es noch besser ... denn dort müßten natürlich all diese
DLL's ihr eigenes Fleckchen finden.
Zu diesem Thema sei auch gleich mal die
OH zitiert.
Für diejenigen, denen die
OH noch unbekannt sein sollte, diese ist das Teil was aufgeht, wenn man die ersten Menüpunkte des Menüs "Hilfe" in Delphi anklickt.
Zitat von
OH:
Image-Basisadresse
Syntax {$IMAGEBASE Zahl}
Die Direktive $IMAGEBASE definiert die Standardladeadresse für eine Anwendung,
DLL oder ein
Package. Das Argument Zahl muß ein 32-Bit-Intergerwert sein, der die Image-Basisadresse angibt. Zahl muß gleich oder größer sein als $00010000; die geringwertigen 16 Bit des Arguments werden ignoriert oder sollten Nullen sein. Zahl muß ein Vielfaches von 64K sein (die entsprechende Hexadezimalzahl muß also mit vier Nullen enden); ist das nicht der Fall, rundet der Compiler die Zahl auf das nächstkleinere Vielfache ab und gibt eine Meldung aus.
Wird ein Modul (Anwendung oder Bibliothek) in den Adressraum eines Prozesses geladen, versucht Windows, das Modul an dessen Standard-Image-Basisadresse abzulegen. Sollte das nicht möglich sein, weil die vorgegebene Adresse bereits belegt ist, wird das Modul an einer Adresse abgelegt, die Windows während der Laufzeit bestimmt (Adressverschiebung).
Für eine Änderung der Image-Basisadresse einer Anwendung gibt es so gut wie nie einen Grund. Dagegen sollten Sie für eine Bibliothek mit der Direktive $IMAGEBASE eine andere als die Standardadresse als Image-Basisadresse angeben, da die Standardadresse $00400000 wahrscheinlich immer belegt sein wird. Der empfohlene Adressbereich für
DLL-Images liegt zwischen $40000000 und $7FFFFFFF. Adressen in diesem Bereich stehen unter Windows NT/2000 und Windows 95/98 immer für Prozesse zur Verfügung.
Sofern Windows eine
DLL oder ein
Package an der zugehörigen Image-Basisadresse ablegen konnte, wird die Ladezeit verringert, weil keine Anpassungen aufgrund von Adressverschiebungen erforderlich sind. Wenn zudem der vorgegebene Adressbereich in mehreren Prozessen, die die Bibliothek benutzen, verfügbar ist, können Teile des
DLL-Image-Codes von den Prozessen gemeinsam benutzt werden, wodurch sich die Ladezeit verringert und weniger Arbeitsspeicher belegt wird.
Adressverschiebung:
Dabei werden nicht einfach nur die
DLL-Daten verschoben, sondern es müssen auch noch alle "hardgecodeten" Pointer, far-Sprungaddressen, ... angepasst werden, da sich ja deren Ziele durch das Verschieben ändern.
Daher kann man sich bestimmt auch ganz gut vorstellen, daß man in solch einem Fall mit einigen Einbußen (Rechenzeit...) rechnen muß.
Es ist also schon "wichtig", daß man seinen
DLL's ein freies Plätzchen sucht.
Zitat von
Luckie:
Um die Adressen innerhalb eines Images zu verschieben, gibt es in jedem Image den sogenannten Realloctable. Es gibt Programme mit denen man diese Tabelle entfernen kann, um die Größe des Images zu reduzieren. Bei Exe-Dateien ist das meist kein Problem, da sie als erstes geladen werden und so die Baseaddress immer frei ist. Bei DLLs ist dies natürlich, wie du erklärt hast, nicht der Fall. Entfernt man aus einer
DLL den Realloctable, kann Windows die neuen Adressen innerhalb der
DLL nicht neu berechnen und die
DLL kann nicht geladen werden.
Zur Definition von {$IMAGEBASE} seien nochmal erwähnt:
* die ImageBase ist ein 32-bit Pointer, welcher auf das Vielfache von 64 KB beschränkt ist.
$xxxx0000 < nur die xxxx sind veränderbar
* die möglichen Werte für die ImageBase sind:
$00010000
bis $7FFF0000
* es wird von Microsoft am Liebsten gesehn, wenn
DLL's in folgendem Bereich abgelegt werden:
$40000000
und $7FFFFFFF
Leider ist dieses aber noch nicht allen bekannt und es gibt tatsächlich Viele, welche ihren
DLL's noch nichtmal ein {$IMAGEBASE} spendieren - wo also die
DLL immer "immer" im Adressbereich der EXE (ab $00400000) liegt und somit verschoben würde.
Hier ist schonmal eine erste kleine Liste von mehr, oder weniger wichtigen "
DLL's".
Code:
$00400000.. Project
$00970000..$009C4FFF XercesXMLDom.dll
$009D0000..$00B7BFFF XercesLib.dll
$00D20000..$00D2AFFF BorlndMM.dll
$1A400000..$1A477FFF UrlMon.dll
$32600000..$32777FFF CC3260MT.dll
$41D00000.. FNS_UCC.dll
$51000000..$5104FFFF DDraw.dll
$5B0F0000..$5B123FFF UXTheme.dll
$5F0D0000..$5F195FFF OpenGL32.dll
$5F1A0000..$5F1B9FFF OLEPro32.dll
$62250000..$6226EFFF MAPI32.dll
$62E10000..$62E17FFF
LPK.dll
$63000000..$63094FFF WinINet.dll
$68FC0000..$68FDDFFF GLU32.dll
$6DA00000..$6DA7BFFF DbgHelp.dll
$70D00000..$70E9FFFF GDIPlus.dll
$71950000..$71A33FFF ComCtl32.dll
$71A80000..$71A90FFF MPR.dll
$72F10000..$72F69FFF USP10.dll
$73B30000..$73B35FFF DCIMan32.dll
$73DC0000..$73DC2FFF LZ32.dll
$74CB0000..$74CD0FFF OLEDlg.dll
$76240000..$7624EFFF MSASN1.dll
$76260000..$762EAFFF Crypt32.dll
$76320000..$76324FFF MSImg32.dll
$76330000..$76349FFF IMM32.dll
$76350000..$76395FFF ComDlg32.dll
$76620000..$76704FFF SetupAPI.dll
$76730000..$76737FFF SHFolder.dll
$76AF0000..$76B1CFFF WinMM.dll
$76BB0000..$76BBAFFF PSAPI.dll
$76BF0000..$76C1AFFF WinTrust.dll
$76C50000..$76C71FFF ImageHlp.dll
$76E40000..$76E4CFFF RTUtils.dll
$76E70000..$76E99FFF TAPI32.dll
$770F0000..$7717AFFF OLEAut32.dll
$77180000..$77299FFF OLE32.dll
$772A0000..$77302FFF ShlWAPI.dll
$773A0000..$77B9DFFF Shell32.dll
$77BD0000..$77BD6FFF Version.dll
$77BE0000..$77C32FFF MSVCRT.dll
$77C40000..$77C7FFFF GDI32.dll
$77C90000..$77D04FFF RPCRT4.dll
$77D10000..$77D9CFFF User32.dll
$77DA0000..$77E39FFF ADVAPI32.dll
$77E40000..$77F36FFF Kernel32.dll
$77F40000..$77FEFFFF NTDLL.dll
Die folgende Prozedur ließt den Speicher der laufenden Anwendung aus
und speichert die erstellte Liste in einer Textdatei ab.
Diese ist für jene, die selber mal in ihrer Anwendung nachschauen wollen.
Als Anhang gibt es diese Prozedur auch nochmal in einer 'netten
Unit verpackt.
Diese
Unit muß als letztes in der Uses-Klausel der
DPR eingetragen werden, da ja vor deren Aufruf auch alle anderen
DLL's geladen werden müssten.
Und wenn man eine, oder mehrere
DLL's dynamisch lädt, dann sollte man nach dem Laden dieser
DLL's die Prozedur "LogModule" nochmals aufrufen.
Werden aber alle
DLL's statisch eingebunden (was ja meistens der Fall ist), dann reicht es aus, wenn nur die
Unit eingebunden wird, da "LogModule" im Initialization-Abschnitt ebenfalls schon einmal aufgerufen wird.
Delphi-Quellcode:
// © 1997-2005 by FNS Enterprize's™
// © 2003-2005 by himitsu @ Delphi-PRAXiS
Uses Windows, SysUtils; // Diese Units mit in der eigenen Uses-Klausel eintragen,
// sofern diese da noch nicht vorhanden sind.
Procedure LogModule;
Var i, i2: Integer;
F: TextFile;
SystemInfo: TSystemInfo;
MemoryBasicInformation: TMemoryBasicInformation;
AllocationBase: Pointer;
ModName: AnsiString;
Begin
AssignFile(F, ParamStr(0) + '.txt');
Rewrite(F);
VirtualQuery(Pointer(0), MemoryBasicInformation, SizeOf(MemoryBasicInformation));
AllocationBase := MemoryBasicInformation.AllocationBase;
i2 := 0;
SetLength(ModName, MAX_PATH);
SetLength(ModName, GetModuleFilenameA(THandle(AllocationBase), @ModName[1], MAX_PATH));
GetSystemInfo(SystemInfo);
i := SystemInfo.dwPageSize;
Repeat
VirtualQuery(Pointer(i), MemoryBasicInformation, SizeOf(MemoryBasicInformation));
If MemoryBasicInformation.AllocationBase <> AllocationBase Then Begin
If AllocationBase <> nil Then WriteLn(F, '$' + IntToHex(i2, 8) + '..$' + IntToHex(i - 1, 8) +
' ' + ExtractFileName(ModName));
AllocationBase := MemoryBasicInformation.AllocationBase;
i2 := i;
SetLength(ModName, MAX_PATH);
SetLength(ModName, GetModuleFilenameA(THandle(AllocationBase), @ModName[1], MAX_PATH));
End;
Inc(i, SystemInfo.dwPageSize);
Until i <= 0;
If AllocationBase <> nil Then WriteLn(F, '$' + IntToHex(i2, 8) + '..$7FFFFFFF ' +
ExtractFileName(ModName));
CloseFile(F);
End;
// zum Schluß muß jetzt noch, "irgendwo" im Programm, die Prozedur "LogModule" aufgerufen werden.
PS: Als Bonus prüft die Prozedur in der
Unit auch noch, ob die gefundene
DLL mit einer der oben genannten
DLL's und deren dort angegebenen ImageBase übereinstimmt.
Außerdem wird ebenfalls noch angegeben, ob die gefundene
DLL verschoben wurde und wo sie eigentlich abgelegt werden wollte.
PSS: Auf die selbe Weiße könnte ein Programm auch nachprüfen, ob eine fremde
DLL eingeschleußt wurde (
DLL-Injektion).
Wenn jemand noch weitere "wichtige"
DLL's und deren Addressbereich kennt, oder der Meinung ist, daß sich einige der oben genannten
DLL's an einen anderen Plätzchen aufhalten, so möchte er/sie es uns bitte mitteilen.