Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   DLL dynamisch laden (https://www.delphipraxis.net/129146-dll-dynamisch-laden.html)

BAMatze 13. Feb 2009 12:20


DLL dynamisch laden
 
Hallo an alle

weiß das Thema wurde hier schon öfter mal behandelt, aber irgendwie hab ich noch nichts gefunden, was meine Fragen zum dynamischen Laden einer DLL beantwortet. Also folgende Situation: Ich habe mehrere DLL´s die ich bisher direkt lade. Hier ein kurzer Ausschnitt, damit ihr wißt, wie ich es mache:

Delphi-Quellcode:
const ExtLib_Verschiebetische = 'MMC.DLL';

function MMC_COM_open(portnumber,baudrate:integer):integer;
            stdcall external ExtLib_Verschiebetische;
Also nichts weiter aufwendiges und funktioniert wunderbar. Allerdings möchte ich jetzt eine verherige überprüfung durchführen, ob sich die Datei in dem Verzeichnis befindet. Hier fangen jetzt meine Probleme an. Habe versucht dies anhand folgendem Threat zu programmieren:

dynamisches Laden einer URL

Da ich aber den genauen Namen nicht kenne, der in der Mitgelieferten DLL verwendet wird, bekomme ich immer eine Fehlermeldung beim Laden einer Funktion aus der DLL. Hier mal das, was ich bisher programmiert habe. Vieleicht ist nur ein Fehler in meinem Code, wenn ja bin ich schon zu betriebsblind :-D um ihn zu finden. Vieleicht hat jemand auch eine andere Idee, wie ich die Überprüfung durchführen kann, ohne die DLL dynamisch zu laden.

Delphi-Quellcode:
unit VT_Funktionen;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ComCtrls;

const maxComport = 100;
      Bautrate = 9600;

type TV_Tische = class
  private
    Tischbmp: TBitmap;
    procedure Funktionenladen;
  public
    DLL_Handle: THandle;
    bBewegung, bkalibriert: boolean;
    iaktuelleTischposition, iZielTischposition, iComport: integer;
    bAngeschlossen: boolean;
    constructor create; reintroduce;
    destructor destroy; override;
    function VT_DLL_suche: boolean;
    function VTischeverbinden: boolean;
end;

Type TVerbindenVT = function (portnumber,baudrate:integer):integer; stdcall;
//function MMC_COM_open(portnumber,baudrate:integer):integer;

{function MMC_COM_open(portnumber,baudrate:integer):integer; stdcall;
function MMC_COM_close:integer; stdcall;}


//function initialisieren_VT: integer;

Var VerbindenVT: TVerbindenVT;

implementation

constructor TV_Tische.create;
begin
  inherited create;
  bBewegung := false;
  bkalibriert := false;
  bAngeschlossen := false;
  iComport := 0;
end;

procedure TV_Tische.Funktionenladen;
begin
  try
    @VerbindenVT := GetProcAddress(DLL_Handle, 'MMC_COM_open')
  finally
    showmessage('Funktion konnte nicht geladen werden');
  end;
end;

function TV_Tische.VTischeverbinden: boolean;
begin
  try
    if @VerbindenVT <> nil then while (VerbindenVT(iComport, Bautrate) <> 0) and (iComPort < maxComPort) do
    begin
      iComport + 1;
    end
  finally
    Showmessage('Die Verbindungsfunktion konnte nicht geladen werden');
  end;
end;

function TV_Tische.VT_DLL_suche: boolean;
begin
  try
    DLL_Handle:=LoadLibrary(PChar(ExtractFilePath(ParamStr(0))+'MMC.DLL'));
    if DLL_Handle <> 0 then
      begin
        result := true;
        Funktionenladen;
      end
    else result := false
  except
    result := false;
  end;
end;

destructor TV_Tische.destroy;
begin
  //FreeLibary(DLL_Handle);
  inherited destroy;
end;



end.
Danke
BAMatze

worker 13. Feb 2009 12:23

Re: DLL dynamisch laden
 
Mit folgendem Code kannst Du die von der DLL exportierten Funktionen ausgeben:
Delphi-Quellcode:
procedure ListDLLExports(const FileName: string; List: TStrings);
type
   TDWordArray = array [0..$FFFFF] of DWORD;
var
   imageinfo: LoadedImage;
   pExportDirectory: PImageExportDirectory;
   dirsize: Cardinal;
   pDummy: PImageSectionHeader;
   i: Cardinal;
   pNameRVAs: ^TDWordArray;
   Name: string;
begin
   List.Clear;
   if MapAndLoad(PChar(FileName), nil, @imageinfo, True, True) then
   begin
      try
         pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress,
          False, IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize);
         if (pExportDirectory <> nil) then
         begin
            pNameRVAs := ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
             DWORD(pExportDirectory^.AddressOfNames), pDummy);
             for i := 0 to pExportDirectory^.NumberOfNames - 1 do
             begin
               Name := PChar(ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
                pNameRVAs^[i], pDummy));
               List.Add(Name);
             end;
         end;
      finally
         UnMapAndLoad(@imageinfo);
      end;
   end;
end;

BAMatze 13. Feb 2009 12:29

Re: DLL dynamisch laden
 
Zitat:

Zitat von worker
Mit folgendem Code kannst Du die von der DLL exportierten Funktionen ausgeben:
Delphi-Quellcode:
procedure ListDLLExports(const FileName: string; List: TStrings);
type
   TDWordArray = array [0..$FFFFF] of DWORD;
var
   imageinfo: LoadedImage;
   pExportDirectory: PImageExportDirectory;
   dirsize: Cardinal;
   pDummy: PImageSectionHeader;
   i: Cardinal;
   pNameRVAs: ^TDWordArray;
   Name: string;
begin
   List.Clear;
   if MapAndLoad(PChar(FileName), nil, @imageinfo, True, True) then
   begin
      try
         pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress,
          False, IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize);
         if (pExportDirectory <> nil) then
         begin
            pNameRVAs := ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
             DWORD(pExportDirectory^.AddressOfNames), pDummy);
             for i := 0 to pExportDirectory^.NumberOfNames - 1 do
             begin
               Name := PChar(ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
                pNameRVAs^[i], pDummy));
               List.Add(Name);
             end;
         end;
      finally
         UnMapAndLoad(@imageinfo);
      end;
   end;
end;

hmm kannst du mal die Bibliotheken noch aufführen, die du verwendet hast, weil einige Funktionen wie LoadedImage, MapandLoad u.s.w nicht erkannt werden ohne weiteres.

worker 13. Feb 2009 12:31

Re: DLL dynamisch laden
 
Sorry

Delphi-Quellcode:
   Windows,
   Messages,
   SysUtils,
   Variants,
   Classes,
   Graphics,
   Controls,
   Forms,
   Dialogs,
   ImageHlp,
   StdCtrls;

BAMatze 13. Feb 2009 12:38

Re: DLL dynamisch laden
 
Danke @worker, funzt gut.

die DLL enthält wirklich die Funktion, so wie ich sie über mein Programm aufrufen möchte. Warum kann diese aber nicht geladen werden? mache ich eventuell etwas grundlegendes falsch?

sirius 13. Feb 2009 12:45

Re: DLL dynamisch laden
 
Wo kommt denn der Fehler (ist extractFilePath evtl. ohne abschließendes "\" ?)

BAMatze 13. Feb 2009 12:51

Re: DLL dynamisch laden
 
Zitat:

Zitat von sirius
Wo kommt denn der Fehler (ist extractFilePath evtl. ohne abschließendes "\" ?)

Hallo @sirius,

also das Handle wird auf jeden Fall gefunden, so wie es aussiegt, da der Aufruf der Procedure Funktionenladen ausgeführt wird. Habe dies trotzdem mal getestet und mit einem zusätzlichen "\" kommt es direkt beim Aufruf der Procedure Funktionenladen zu einer Exception.

RWarnecke 13. Feb 2009 12:56

Re: DLL dynamisch laden
 
So rufe ich meine AboutBox-Form auf, die in einer DLL ist :
Delphi-Quellcode:
type
  TFormDLLBox  = procedure(appHandle: THandle); stdcall;

procedure TMainForm.Act_AboutBoxExecute(Sender: TObject);
var
  hDLL: THandle;
  AboutBoxWindow: TFormDLLBox;
begin
  hDLL := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + HLPBOXES));
  if hDLL <> 0 then
  begin
    try
      AboutBoxWindow := GetProcAddress(hDLL, 'AboutBox');
      AboutBoxWindow(Application.Handle);
    finally
      FreeLibrary(hDLL);
    end;
  end
  else
    Application.MessageBox(PChar('Die Datei "' + HLPBOXES + '" konnte nicht gefunden werden.' + #13#10+
      'Bitte überprüfen Sie Ihr Programmverzeichnis'), 'Hinweis', MB_OK);
end;
Das ganze sollte auch auf eine normale Funktion oder Procedure Deiner DLL anwendbar sein.

sirius 13. Feb 2009 12:57

Re: DLL dynamisch laden
 
Was kommen denn für Fehlermeldungen?

Außerdem hast du Finally, wo du anscheinend Except haben willst.

ChrisE 13. Feb 2009 13:01

Re: DLL dynamisch laden
 
Zitat:

Zitat von sirius
Außerdem hast du Finally, wo du anscheinend Except haben willst.

Auserdem machst du später Deinen Test auf nil. Ich würde sicherheitshalber den Zeiger wirklich noch vor GetProcAdress auf nil setzten bzw im EXCEPT-Fall spätestens.

Gruß, Chris

BAMatze 13. Feb 2009 13:42

Re: DLL dynamisch laden
 
Zitat:

Zitat von sirius
Was kommen denn für Fehlermeldungen?

Außerdem hast du Finally, wo du anscheinend Except haben willst.

gibt es auch eine Solche funktion, wo man sich anschauen kann, was auf den Com-Schnitt-Stellen liegt, wie für das auslesen der Export-Funktionen einer DLL?

Zoot 13. Feb 2009 14:04

Re: DLL dynamisch laden
 
Muss es nicht "VerbindenVT := " anstatt "@VerbindenVT := " heißen?

BAMatze 13. Feb 2009 14:07

Re: DLL dynamisch laden
 
Zitat:

Zitat von Zoot
Muss es nicht "VerbindenVT := " anstatt "@VerbindenVT := " heißen?

also die Procedure so wie ich sie dort geschrieben hab funktioniert, hatte einen kleinen Fehler in der Unit, die diese Funktion aufgerufen hat. Leider stehe ich jetzt vor einem anderen Problem, dass dies anscheinend eine Allgemeine Funktion ist, mit der die Kommunikation mit einem Comport aufgebaut wird, der auch eine angeschlossene Komponente hat. Eigentlich dachte ich, dass es nur die Kommunikation mit einer bestimmten Komponente aufnimmt.

Danke @ all

BAMatze 16. Feb 2009 11:12

Re: DLL dynamisch laden
 
Hallo nochmal an alle,

muss diesen Thread nochmal aufnehmen, da sich neue Probleme auftun. Ich habe mit den Hinweisen, die hier schon gegeben wurden die Funktionen ausgelesen, die mir die mitgelieferte DLL gibt. Diese habe ich nach dem im Eingangsthreat vorliegenen Muster (Delphi-Code) eingebunden. Jetzt hab ich aber festgestellt, dass er selbst auf dem Comport für das Gerät mit den Proceduren aus der DLL, so wie ich sie eingebunden hab nicht anspringt. Hab aber auch nach längerem Suchen keinen Fehler gefunden. Fällt jemanden vieleicht etwas auf, was ich übersehen hab.

vielen Dank
BAMatze

sirius 17. Feb 2009 09:19

Re: DLL dynamisch laden
 
Ist schwer von hier aus dahinterzusteigen, da die wenigsten deine benutzte API/DLL kennen.
Hast du die Rückgabewerte geprüft?

BAMatze 19. Feb 2009 07:00

Re: DLL dynamisch laden
 
@sirius

Habe das erstmal wie folgt gelöst. Weiß nicht, ob das so wirklich logisch ist aber bisher tut es seine Tätigkeit. Hier erstmal der Code:

in der ersten Unit wird geprüft, ob die DLL vorhanden ist. Wenn sie vorhanden ist, wird eine Komponente kreiert, die in der 2. Unit eingeführt wird.

Delphi-Quellcode:
function THUnterthread.DLL_suche(const sDatei: string): THandle;
var DLL_Handle: THandle;
begin
  try
    DLL_Handle:=LoadLibrary(PChar(ExtractFilePath(ParamStr(0))+sDatei));
    if DLL_Handle <> 0 then result := DLL_Handle
    else result := 0
  except
    result := 0;
  end;
end;

procedure THUnterthread.Verschiebetische_initialisieren;
begin
  iThreadmsg := 2;
  if DLL_suche('MMC.DLL') <> 0 then
    begin
      V_Tische := TV_Tische.create;
      iThreadmsg := 3;
      VTComPort_ermitteln;
      Komponente_anlegen('MMC.DLL','Verschiebetische', VTComPort_ermitteln);
    end
  else iThreadmsg := 4;
end;
In der 2. Unit wird die TV_Tische-Class eingeführt und hier sämtliche benötigten Funktionen statisch aus der DLL ausgelesen. Das war leider so nötig, wegen den Problemen, die beim dynamischen Laden aufgetreten sind.

Delphi-Quellcode:
unit VT_Funktionen;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ComCtrls;

const maxComPort = 100;
      Bautrate = 9600;
      ExtLib = 'MMC.DLL';
      constMillimeter = 6400;

type TV_Tische = class
  private
    Tischbmp: TBitmap;
    function Bewegtsich(const Kanal: integer): boolean;
    function Bremsen: boolean;
    function Geschwindigkeit_festlegen(const iGeschwindigkeit: integer): boolean;
    function Beschleunigung_festlegen(const iBeschleunigung: integer): boolean;
  public
    DLL_Handle: THandle;
    bBewegung, bkalibriert: boolean;
    iaktuelleTischposition, iZielTischposition, iComport, iFehlercode: integer;
    bAngeschlossen: boolean;
    constructor create; reintroduce;
    destructor Destroy; override;
    function VTische_verbinden(Comport: integer): boolean;
    function Kalibrierung(const Kanal: integer): boolean;
    function BewegenABS(dneuPos: double): boolean; overload;
    function BewegenABS(const KaliPos: string): boolean; overload;
    function BewegenABS(dneuPos: double; iGeschwindigkeit: integer): boolean; overload;
    function BewegenABS(dneuPos: double; iGeschwindigkeit, iBeschleunigung: integer):boolean; overload;


end;

{***********************************************************************************
 *                  allgemeine Funktionen für Verschiebetische                    *
 ***********************************************************************************}



{**********************************Ende Funktionen**********************************}

{***********************************************************************************
 *                        Funktionen für Verschiebetische                         *
 ***********************************************************************************}

function MMC_COM_open(portnumber,baudrate:integer):integer;
            stdcall external ExtLib;
function MMC_COM_close:integer;
            stdcall external ExtLib; //
function MMC_COM_setBuffer:integer;
            stdcall external ExtLib; //
function MMC_sendString(pCmd:pChar):integer;
            stdcall external ExtLib; //
function MMC_sendCommand(pCmd:pChar):integer;
            stdcall external ExtLib; //
function MMC_getPos:integer;
            stdcall external ExtLib; //
function MDC_getPosErr:integer;
            stdcall external ExtLib; //
function MMC_getVal(query:integer):integer;
            stdcall external ExtLib; //
function MMC_getReport(pCmd,psRead:PChar):integer;
            stdcall external ExtLib; //
function MMC_getStringCR(psRead:PChar):integer;
            stdcall external ExtLib; //
function MDC_moving:integer;
            stdcall external ExtLib; //
function MST_moving:integer;
            stdcall external ExtLib; //
function MMC_initNetwork(maxaxis:integer):integer;
            stdcall external ExtLib; //
function MMC_select(newaxis:integer):integer;
            stdcall external ExtLib; //
function MMC_setDevice(newaxis:integer):integer;
            stdcall external ExtLib; //
function MMC_COM_clear:integer;
            stdcall external ExtLib; //
function MMC_COM_EOF:integer;
            stdcall external ExtLib; //
function MMC_getSTB(byteno:integer):integer;
            stdcall external ExtLib; //
function MDC_waitStop:integer;
            stdcall external ExtLib; //
function MST_waitStop:integer;
            stdcall external ExtLib; //
function MMC_getDLLversion:integer;
            stdcall external ExtLib;
function MMC_moveA(axis,position:integer):integer;
            stdcall external ExtLib;
function MMC_moveR(axis,shift:integer):integer;
            stdcall external ExtLib;
function MMC_getMacro(macno:integer;content:PChar):integer;
            stdcall external ExtLib;
function MMC_globalBreak:integer;
            stdcall external ExtLib;

{**********************************Ende Funktionen**********************************}


implementation

constructor TV_Tische.create;
begin
  inherited create;
  bBewegung := false;
  bkalibriert := false;
  bAngeschlossen := false;
end;

function TV_Tische.Geschwindigkeit_festlegen(const iGeschwindigkeit: Integer): boolean;
begin
  try
    MMC_sendCommand(PAnsiChar('SV'+inttostr(iGeschwindigkeit)));
    result := true;
  except
    result := false;
  end;
end;

function TV_Tische.Beschleunigung_festlegen(const iBeschleunigung: Integer): boolean;
begin
  try
    MMC_sendCommand(PAnsiChar('SA'+inttostr(iBeschleunigung)));
    result := true;
  except
    result := false;
  end;
end;

function TV_Tische.Bremsen: boolean;
begin
  try
    Geschwindigkeit_festlegen(0);
    Beschleunigung_festlegen(0);
    MMC_sendCommand('AB')
  except

  end;
end;

function TV_Tische.BewegenABS(dneuPos: double): boolean;
begin
  Geschwindigkeit_festlegen(500);
  Beschleunigung_festlegen(500);
  MMC_sendCommand(PAnsiChar('MA' + inttostr(round(dneuPos*constMillimeter))));
end;

function TV_Tische.BewegenABS(const KaliPos: string): boolean;
begin
  Geschwindigkeit_festlegen(1000);
  Beschleunigung_festlegen(1000);
  MMC_sendCommand(PAnsiChar(KaliPos));
end;

function TV_Tische.BewegenABS(dneuPos: double; iGeschwindigkeit: integer): boolean;
begin
  Geschwindigkeit_festlegen(iGeschwindigkeit);
  Beschleunigung_festlegen(500);
  MMC_sendCommand(PAnsiChar('MA' + inttostr(round(dneuPos*constMillimeter))));
end;

function TV_Tische.BewegenABS(dneuPos: Double; iGeschwindigkeit: Integer; iBeschleunigung: Integer): boolean;
begin
  Geschwindigkeit_festlegen(iGeschwindigkeit);
  Beschleunigung_festlegen(iBeschleunigung);
  MMC_sendCommand(PAnsiChar('MA' + inttostr(round(dneuPos*constMillimeter))));
end;

function TV_Tische.VTische_verbinden(Comport: integer): boolean;
begin
  try
    MMC_COM_open(Comport, Bautrate);
    BewegenABS('FE2');
    if MST_moving = 1 then
      begin
        Bremsen;
      end
  except
    result := false;
  end;
end;

function TV_Tische.Kalibrierung(const Kanal: integer): boolean;
begin
  try
    MMC_setDevice(Kanal);
    BewegenABS('FE2');
  except
  end;
end;

function TV_Tische.Bewegtsich(const Kanal: integer): boolean;
begin
  MMC_setDevice(Kanal);
  if MST_moving = 1 then result := true
  else result := false;
end;

destructor TV_Tische.Destroy;
begin
  Bremsen;
  MMC_COM_close;
  inherited Destroy;
end;
end.
Im Nachhinein ist es glaube ich auch möglich dies alles dynamisch zu machen. Werde dies auch gleich mal probieren. Das Problem an sich ist, dass ich wenn ich für die meisten boolschen-DLL-Funktionen die mir die DLL bietet auf einer Com-Schnittstelle, an der ein anderes Gerät angeschlossen ist (bei mir war das z.B. die Maus) meist den Wert true zurück bekam. Dadurch wurde die Verifizierung des richtigen Com-Ports sehr schwierig. Deswegen habe ich die MST_moving benutzt, da dies die einzige von mir gefundene DLL-Funktion ist, die wirklich true ist, wenn die Tische angesprochen werden.

Wie gesagt, mit den Erkenntnissen sollte es jetzt auch möglich sein, alles dynamisch zu machen. Meine neue Frage hierzu wäre:

Ist dies wirklich noch nötig oder kann ich es so lassen?

Vielen Dank
BAMatze

sirius 19. Feb 2009 08:31

Re: DLL dynamisch laden
 
Ich denke in deinem Fall ist statisches Einbinden der DLL besser geeignet. Durch dynamisches Einbinden kann man halt nur sicherstellen, dass das Programm auch läuft, wenn die DLL nicht vorhanden ist. Man kann das Fehlen der DLL quasi abfangen. Wichtig dazu ist, wie gesagt, dass das Programm auch ohne diese DLL funktionieren würde. Das ist aber bei dir nicht der Fall. Deswegen -> statisch einbinden. Wenn die DLL tatsächlich nicht existiert schmeißt Windows bereits beim Laden deines Programms eine Fehlermeldung raus und der User kann sich kümmern. Etwas anderes kannst du ja auch nicht machen.

Wenn du allerdings die DLL statisch einbindest, brauchst du im Programm nicht mehr zu testen, ob sie wirklich da ist (mit allen Funktionen die du benötigst). Das hat, wie gesagt, Windows schon für dich gemacht.
Windows macht das übrigens nicht so aus Jux und Dallerei, sondern es muss ja die Importtabelle deiner EXE für alle statisch eingebundenen Funktionen füllen.

BAMatze 19. Feb 2009 09:50

Re: DLL dynamisch laden
 
@sirius

Du hast Recht. Also werde ich dies nochmal ändern müssen. Möchte, dass das Programm auch startet, wenn die DLL nicht vorhanden ist. Mich verwirrt jetzt eigentlich nur, dass in der Entwicklungsumgebung dies nicht passiert. Hier hatte ich das eigentlich schon so abgefangen, dass er mir gesagt hat im Programm, ob die Datei vorhanden ist oder nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:16 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz