MSCH - So 12.10.03 10:48
Titel: DLL Programmierung inkl. Static and dynamic linking
So, nachdem ich einige Threads zu DLLs gefunden habe, wo immer noch Unklarheiten der Programmierung besteht, habe ich einfach mich mal rangesetzt, und ein kleines Demo versucht zu pinseln.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| library test;
uses SysUtils, Classes;
{$R *.res}
function CanWork: Boolean; export; begin result:= true; end;
exports CanWork index 1 name 'canWork';
begin end. |
so, nun sagen wir unser Anwendung wie sie diese Testfunktion aufrufen kann, damit das ganze Speicherschonend wird, nehmen wir das dynamische Linken.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
| unit Main; uses .....; type TcanWork = function:Boolean; function TMain.GetTestDLLCanWork:Boolean; var S : String; P : Array[0..512] of Char; Proc : TFarProc; begin S:= IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))+'test.dll'; if FileExists(S) then begin StrPcopy(P,S); DllHandle:=LoadLibrary(P); if DLLHandle<>null then begin Proc:= GetProcAddress(DLLhandle,'canWork'); if Proc<>nil then result:=TCanWork(Proc); else result:=false; FreeLibrary(DLLHandle); end else Result:=False; end else Result:=False; end; |
jetzt wollen wir aber eine Form in einer DLL nutzen, zum Beispiel um ein Logo-Fenster zu erstellen
also erstellen wir erstmal eine Form, die unser Logo darstellt und testen diese erstmal in unserer Anwendung.
(Im folgenden soll das Fenster TLogoForm heissen und liegt in der Unit TLOGO_Unit)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| library test; uses SysUtils, Classes, TLOGO_Unit in 'TLogo_Unit.pas' ;
{$R *.res}
function CanWork: Boolean; export; begin result:= true; end;
procedure ShowLogo(Parent:Pointer);export; begin try LogoForm:= TLogoForm.Create(Parent); LogoForm.ShowModal; finally LogoForm.Free; end; end;
exports CanWork index 1 name 'canWork', ShowLogo index 2 name 'showLogo'; begin end. |
In unserem Programm erweitern wir nun die Typdeklaration
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| unit Main; uses .....; type TcanWork = function:Boolean; TShowLogo= procedure(Parent:Pointer); procedure TMain.ShowMyLogo; var S : String; P : Array[0..512] of Char; Proc : TFarProc; begin S:= IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))+'test.dll'; if FileExists(S) then begin StrPcopy(P,S); DllHandle:=LoadLibrary(P); if DLLHandle<>null then begin Proc:= GetProcAddress(DLLhandle,'showLogo'); if Proc<>nil then TShowLogo(Proc); FreeLibrary(DLLHandle); end; end end; |
so. Hier noch die Deklaration des statischen AUfrufs. D.h. durch den BS-Loader wird im Header der EXE Datei ermittelt, welche DLLs zur Laufzeit
sofort in den Anwendungsspeicher geladen werden müssen. Diese DLL müssen dem BS bekannt sein. Also im Anwendungspfad oder Systemverzeichnis liegen.
Sollte die DLL nicht gefunden werden, wird das gesamte PROGRAMM NICHT AUSGEFÜHRT.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| unit Main; uses .....;
function CanWork: Boolean; external 'Test.DLL' name 'canWork'; procedure ShowLogo; external 'Test.DLL' name 'showLogo';
procedure TMain.ShowMyLogo; begin ShowLogo; end; |
grez
msch
Motzi - So 12.10.03 13:35
Hast du den Code auch getestet? Mir sind da nämlich ein paar Sachen aufgefallen:
Delphi-Quelltext
1: 2: 3: 4: 5:
| var P : Array[0..512] of Char;
StrPcopy(P,S); DllHandle:=LoadLibrary(P); |
Ist zwar nicht direkt ein Fehler, aber solche zusätzlichen Sachen kannst du dir sparen indem du direkt nach PChar castest oder die Ähnlichkeit der Delphi-Strings zum PChar ausnützt:
Delphi-Quelltext
1: 2: 3:
| DllHandle := LoadLibrary(PChar(S)); DllHandle := LoadLibrary(@S[1]); DllHandle := LoadLibrary(Pointer(S)); |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type canWork = function:Boolean;
Proc : TFarProc;
Proc := GetProcAddress(DLLhandle, 'canWork'); if Proc <> nil then Result := CanWork(Proc); |
Wieso verwendest du TFarProc wenn du eh einen eigenen Typen canWork deklariert hast? (Typendeklarationen unter Delphi sollten übrigens immer mit einem großen T beginnen!)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type TCanWork = function: Boolean;
Proc : TCanWork
Proc := GetProcAddress(DLLhandle, 'canWork' ); if Proc <> nil then Result := Proc; |
Delphi-Quelltext
1: 2:
| procedure ShowLogo(Parent:Pointer); export; type ShowLogo= procedure; |
Abgesehen von den Punkten die ich schon vorher angesprochen hab hast du hier 2 unterschiedliche Prozedur-Header. In der Dll-Deklaration hast du einen Parameter "Parent", in der Typen-Deklaration für den dyn. Import hast du jedoch keinen Parameter!
Beim statischen Import dasselbe.. die importierte Funktion ShowLogo hat in der importierten Version keine Parameter!
Nachdem Delphi standardmäßig die Register-Aufrufkonvention benutzt und dabei bis zu 3 Parameter über die Register übergibt bedeutet das, dass die Dll-Funktion ShowLogo jetzt einfach den Wert nimmt, der im Register EAX steht. Ob dieser jetzt aber einen in dem Zusammenhang sinnvollen (gültigen) Wert hat ist aber eine andere Frage...