Autor Beitrag
MSCH
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1448
Erhaltene Danke: 3

W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
BeitragVerfasst: So 12.10.03 10:48 
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.

ausblenden 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;
// beinhaltet eine Funktion die einen Bool zurück gibt
// nur als Bespiel

uses
  SysUtils,
  Classes;

{$R *.res}

function CanWork: Boolean; export;
begin
  result:= true;
end;

exports
  CanWork index 1 name 'canWork';

begin
// Hier kann Code stehen, der ausgeführt wird, wenn die DLL geladen wird
// z.b. eine About-Box oder ein Logo-Window
end.


so, nun sagen wir unser Anwendung wie sie diese Testfunktion aufrufen kann, damit das ganze Speicherschonend wird, nehmen wir das dynamische Linken.

ausblenden 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; // unsere DLL Funktion als Typdeklaration

function TMain.GetTestDLLCanWork:Boolean;
var
    S     : String;
    P     : Array[0..512of Char;
    Proc  : TFarProc;
begin
  S:= IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))+'test.dll'// wir nehmen mal an, dass die DLL sich im Programmverzeichnis befindet
  if FileExists(S) then begin // sie ist da !
    StrPcopy(P,S); // String to PChar
    DllHandle:=LoadLibrary(P); // DLL Laden
    if DLLHandle<>null then begin // DLL konnte geladen werden
      Proc:= GetProcAddress(DLLhandle,'canWork'); // Adresse der Funktion holen
      if Proc<>nil then  // Adresse gefunden 
        result:=TCanWork(Proc); // Aufrufen und Result setzen
      else 
        result:=false; // nicht aufrufbar also defaultwert zurückgeben
      FreeLibrary(DLLHandle);
    end else
      Result:=False; // DLL konnte nicht geladen werden, also Defaultwert zurückgeben
  end else
    Result:=False; // DLL konnte nicht gefunden werden
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)

ausblenden 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' {TLogoForm};

{$R *.res}

function CanWork: Boolean; export;
begin
  result:= true;
end;

// Parent ist eigentlich TComponent; aber wir sparen uns dadurch etwas Schreiberei
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

ausblenden 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; // unsere DLL Funktion als Typdeklaration
  TShowLogo= procedure(Parent:Pointer);        // dito

procedure TMain.ShowMyLogo;
var
    S     : String;
    P     : Array[0..512of Char;
    Proc  : TFarProc;
begin
  S:= IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))+'test.dll'// wir nehmen mal an, dass die DLL sich im Programmverzeichnis befindet
  if FileExists(S) then begin // sie ist da !
    StrPcopy(P,S); // String to PChar
    DllHandle:=LoadLibrary(P); // DLL Laden
    if DLLHandle<>null then begin // DLL konnte geladen werden
      Proc:= GetProcAddress(DLLhandle,'showLogo'); // Adresse der Funktion holen
      if Proc<>nil then  // Adresse gefunden 
        TShowLogo(Proc); // Aufrufen 
      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.
ausblenden 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; // Aufrufen 
end;



grez
msch

_________________
ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?


Zuletzt bearbeitet von MSCH am So 12.10.03 14:47, insgesamt 2-mal bearbeitet
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: So 12.10.03 13:35 
Hast du den Code auch getestet? Mir sind da nämlich ein paar Sachen aufgefallen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
var
  P     : Array[0..512of Char;

StrPcopy(P,S); // String to PChar
DllHandle:=LoadLibrary(P); // DLL Laden

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:
ausblenden Delphi-Quelltext
1:
2:
3:
DllHandle := LoadLibrary(PChar(S));
DllHandle := LoadLibrary(@S[1]);
DllHandle := LoadLibrary(Pointer(S));


ausblenden 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!)
ausblenden 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;


ausblenden Delphi-Quelltext
1:
2:
procedure ShowLogo(Parent:Pointer); export// Dll-Export
type ShowLogo= procedure// Type-Deklaration für Dll-Import

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...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
MSCH Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1448
Erhaltene Danke: 3

W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
BeitragVerfasst: So 12.10.03 14:47 
Ich hab das ganze aus einem funktionierenden Projekt kopiert.

Deine Reklamationen sind eingebaut.
allerdings habe ich auf einiges Verzichtet:
LoadLibrary(P), TFarProc etc. der besseren Lesbarkeit und Verständnis halber so gelassen

_________________
ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?