Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#1

Delayed Loaded Libraries?

  Alt 16. Nov 2012, 10:37
Delphi-Version: XE2
Verwendet dieses denn schon jemand ausgiebig?

Ich hab es grade so für mich (wieder)gefunden und will es demnächst einsetzen.
Im Prinzip ist es eine wunderschöne Sache (vorallem man man Dieses auch noch komplett nutzt).

Statisches Laden ist ja nicht immer möglich/erwünscht.
Delphi-Quellcode:
procedure MyProc(...);
...

procedure MyProc(...);
external 'My.dll'; external 'My.dllname 'MyProc';
;

Und dynamisches Laden sieht häßlich aus.
Delphi-Quellcode:
var MyProc = procedure(...);

// und dann irgendwo ein LoadLibrary und GetProcAddress
Jetzt könnte man noch anfangen und Wrapper-Prozeduren drumrumpacken, damit es nach außen wieder ein "schöner" Prozeduraufruf wird
und vorallem damit niemand "ausversehn" diese Variable überschreibt.


Sooooooo, im Prinzip reicht es so schon aus: (LoadLibrary+GetProcAddress)
Delphi-Quellcode:
function MyFunction(...): PChar;

function MyFunction(...): PChar;
external 'my.dllname 'MyFunction' delayed;
Schon wird die DLL erst beim ersten Aufruf der Funktion geladen.

Und nun nur noch das Endladen (FreeLibrary):
Delphi-Quellcode:
UnloadDelayLoadedDLL('my.dll');
// Hier bitte ganz genau die Dokumentation (MSDN) durchlesen, da es ein/zwei Dinge zu beachten gibt.
Um das jetzt noch ein bissl OOPiger hinzubekommen, hab ich das Ganze in einem Record gekapselt, wo der erste Parameter der Record selber ist.
Man könnte aber auch statische Klassen-Prozeduren in (leeren) Records oder (abstrakten) Klassen nutzen.
Delphi-Quellcode:
procedure Test(Sender: TObject);
var
  F: TTestFunc;
begin
  F.Time := Now;
  ShowMessage(F.ToTime);

  F := Now;
  ShowMessage(F.ToDate + ' ' + F.ToTime);

  F.Unload;
end;
Delphi-Quellcode:
type
  TTestFunc = record
  public const
    TheDLL = 'DelayedLoadDLL.dll';
  public
    Time: TDateTime;
    function ToTime: PChar; // die beiden Funktionen in einem Record verpackt
    function ToDate: PChar; // function DateToStr(Time: PDataTime): PWideChar;
    class procedure Unload; static;
    class operator Implicit(const Rec: TDateTime): TTestFunc;
    class operator Implicit(const Rec: TTestFunc): TDateTime;
  end;

class operator TTestFunc.Implicit(const Rec: TDateTime): TTestFunc;
begin
  Result.Time := Rec;
end;

class operator TTestFunc.Implicit(const Rec: TTestFunc): TDateTime;
begin
  Result := Rec.Time;
end;

function TTestFunc.ToDate: PChar;
external TTestFunc.TheDLL name 'DateToStr' delayed;

function TTestFunc.ToTime: PChar;
external TTestFunc.TheDLL name 'TimeToStr' delayed;

class procedure TTestFunc.Unload;
type
  DLIUProc = function(szDll: PAnsiChar): LongBool; stdcall;
begin
  if not DLIUProc(UnloadDelayLoadedDLLPtr)(TheDLL) then
    OutputDebugString('DLL was not unloaded');
end;
Im Anhang ist uch noch eine weitere "Klasse" (TDelayedLoadLibrary) drin, welche als Hook für die DLI-PI diehnt.
Denn bei der originalen API kann man sich nur einmal registrieren (wie z.B. bei den alten Application.OnMessage) und diese Klasse ist nun mein "TApplicationEvents".
(ohne würde es schwerer das Event zu nutzen, wenn man dieses mehrfach im Programm bräuchte, vorallem wenn dann in irgendeiner "Fremdkomponente" sich jemand zwar registriert, aber den Vorgänger vergisst aufzurufen )

PS: XE3 fehlt immernoch, aber ich gleub das gibt's auch schon in weilchen, also egal, aber irgendwas muß man ja auswählen, damit kein D5 dasteht




[edit]
bevor ich's wieder vergesse ... hier noch eine Debugausgabe zum Code in der DelayedLoadProcU.pas :
(ein bissl aufgesplittet nach Funktionen und mit Positionsmarken versehn)
Code:
Prozess DelayedLoad.exe (4568)



Thread-Start: Thread-ID: 8668.
Prozessstart: ...\DelayedLoad.exe. Basisadresse: $00400000.
Modul laden: DelayedLoad.exe. Enthält Debug-Infos. Basisadresse: $00400000.
Modul laden: ntdll.dll. Ohne Debug-Infos. Basisadresse: $77020000.
Modul laden: KERNEL32.dll. Ohne Debug-Infos. Basisadresse: $74990000.
Modul laden: KERNELBASE.dll. Ohne Debug-Infos. Basisadresse: $76710000.
Modul laden: OLEAUT32.dll. Ohne Debug-Infos. Basisadresse: $74B40000.
Modul laden: ole32.dll. Ohne Debug-Infos. Basisadresse: $765B0000.
Modul laden: msvcrt.dll. Ohne Debug-Infos. Basisadresse: $74F80000.
Modul laden: GDI32.dll. Ohne Debug-Infos. Basisadresse: $76200000.
Modul laden: USER32.dll. Ohne Debug-Infos. Basisadresse: $762F0000.
Modul laden: ADVAPI32.dll. Ohne Debug-Infos. Basisadresse: $763F0000.
Modul laden: SECHOST.dll. Ohne Debug-Infos. Basisadresse: $74790000.
Modul laden: RPCRT4.dll. Ohne Debug-Infos. Basisadresse: $74BD0000.
Modul laden: SspiCli.dll. Ohne Debug-Infos. Basisadresse: $74700000.
Modul laden: CRYPTBASE.dll. Ohne Debug-Infos. Basisadresse: $746F0000.
Modul laden: LPK.dll. Ohne Debug-Infos. Basisadresse: $74980000.
Modul laden: USP10.dll. Ohne Debug-Infos. Basisadresse: $75E90000.
Modul laden: VERSION.dll. Ohne Debug-Infos. Basisadresse: $721F0000.
Modul laden: COMCTL32.dll. Ohne Debug-Infos. Basisadresse: $73570000.
Modul laden: SHLWAPI.dll. Ohne Debug-Infos. Basisadresse: $76290000.
Modul laden: SHELL32.dll. Ohne Debug-Infos. Basisadresse: $751F0000.
Modul laden: WINSPOOL.DRV. Ohne Debug-Infos. Basisadresse: $737F0000.
Modul laden: IMM32.dll. Ohne Debug-Infos. Basisadresse: $75190000.
Modul laden: MSCTF.dll. Ohne Debug-Infos. Basisadresse: $75F70000.
Modul laden: UxTheme.dll. Ohne Debug-Infos. Basisadresse: $73950000.
Modul laden: dwmapi.dll. Ohne Debug-Infos. Basisadresse: $74650000.
Modul laden: WTSAPI32.dll. Ohne Debug-Infos. Basisadresse: $741F0000.
Modul laden: WINSTA.dll. Ohne Debug-Infos. Basisadresse: $73C60000.
Thread-Start: Thread-ID: 4148.
Thread-Start: Thread-ID: 13156.



1:
Debug-Ausgabe: ProcAddr: $005A83F8
2:
Debug-Ausgabe: dliStartProcessing: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: dliNotePreLoadLibrary: DelayedLoadDLL.dll TimeToStr
Modul laden: DelayedLoadDLL.dll. Ohne Debug-Infos. Basisadresse: $04B60000.
Debug-Ausgabe: Load DLL: DelayedLoadDLL.dll
Debug-Ausgabe: dliNotePreGetProcAddress: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: dliNoteEndProcessing: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: 06:06:07.165
3:
Debug-Ausgabe: ProcAddr: $005A83F8 <> $04C2D568
4:
Debug-Ausgabe: Unload DLL: DelayedLoadDLL.dll
Modul entladen: DelayedLoadDLL.dll.
5:
Debug-Ausgabe: ProcAddr: $005A83F8 = $005A83F8
6:
Debug-Ausgabe: dliStartProcessing: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: dliNotePreLoadLibrary: DelayedLoadDLL.dll TimeToStr
Modul laden: DelayedLoadDLL.dll. Ohne Debug-Infos. Basisadresse: $04B60000.
Debug-Ausgabe: Load DLL: DelayedLoadDLL.dll
Debug-Ausgabe: dliNotePreGetProcAddress: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: dliNoteEndProcessing: DelayedLoadDLL.dll TimeToStr
Debug-Ausgabe: 06:07:07.178
7:
Debug-Ausgabe: dliStartProcessing: DelayedLoadDLL.dll DateToStr
Debug-Ausgabe: dliNotePreGetProcAddress: DelayedLoadDLL.dll DateToStr
Debug-Ausgabe: dliNoteEndProcessing: DelayedLoadDLL.dll DateToStr
Debug-Ausgabe: 2012-11-15
8:
Debug-Ausgabe: ProcAddr: $005A83F8 <> $04C2D568 = $04C2D568



Modul laden: ole32.dll. Ohne Debug-Infos. Basisadresse: $05130000.
Modul entladen: ole32.dll.
Debug-Ausgabe: dliStartProcessing: DWMAPI.DLL DwmIsCompositionEnabled
Debug-Ausgabe: dliNotePreLoadLibrary: DWMAPI.DLL DwmIsCompositionEnabled
Debug-Ausgabe: dliNotePreGetProcAddress: DWMAPI.DLL DwmIsCompositionEnabled
Debug-Ausgabe: dliNoteEndProcessing: DWMAPI.DLL DwmIsCompositionEnabled
Debug-Ausgabe: dliStartProcessing: DWMAPI.DLL DwmExtendFrameIntoClientArea
Debug-Ausgabe: dliNotePreGetProcAddress: DWMAPI.DLL DwmExtendFrameIntoClientArea
Debug-Ausgabe: dliNoteEndProcessing: DWMAPI.DLL DwmExtendFrameIntoClientArea
(wie man sieht, nutzt auch Emba selbst diese Technik)

Programm und Fenster sind fertig geladen
(den Rest hab'sch weggelassen)
Angehängte Dateien
Dateityp: 7z DelayedLoad.7z (62,2 KB, 27x aufgerufen)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat