Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi exportieren von Strukturen aus DLLs... (https://www.delphipraxis.net/68651-exportieren-von-strukturen-aus-dlls.html)

heiopei 3. Mai 2006 11:32


exportieren von Strukturen aus DLLs...
 
hallo,
ich möchte aus einer DLL eine Struktur exportieren, die Informationen zur DLL ausgibt; dies mache ich per aufruf eine funktion.

Hier der Code der DLL:
Delphi-Quellcode:
library DLLTest;

{$R *.res}
{$R meine_resource.res}

type
//Definition einer speziellen Karte
  TSpezielle = record
    Name: PChar;
    Index: Word;
  end;

  TSpezielleArray = array of TSpezielle;

  //Definition der Struktur einer KartenSammlung-Information
  TKartenSammlungInfo = record
    Name: PChar;                    //Name der Kartensammlung
    Version: PChar;                 //Version der Kartensammlung
    Karten: Byte;                   //Anzahl der Karten (ohne spezielle)
    Spezielle: TSpezielleArray;     //vorhandene spezielle Karten mit Name und
                                     //Index
  end;

function DLLSammlungInfo(): TKartenSammlungInfo; stdcall;
begin
  with result do
  begin
    Name := 'Test der DLL';
    Version := '1.0';
    Karten := 32;
    SetLength(Spezielle, 2);
    Spezielle[0].Name := 'Speziell1';
    Spezielle[0].Index := 65534;
    Spezielle[1].Name := 'Speziell2';
    Spezielle[1].Index := 65535;
  end;
end;

exports
  DLLSammlungInfo;

end.
im hauptprogramm lade ich nun diese DLL (dynamisch) und übernehme den Rückgabewert(die Informationen) der Funktion.

Hier der Code des Hauptprogramms:

Delphi-Quellcode:
type
  //Definition einer speziellen Karte
  TSpezielle = record
    Name: PChar;
    Index: Word;
  end;

  TSpezielleArray = array of TSpezielle;

  //Definition der Struktur einer KartenSammlung-Information
  TKartenSammlungInfo = record
    Name: PChar;                    //Name der Kartensammlung
    Version: PChar;                 //Version der Kartensammlung
    Karten: Byte;                   //Anzahl der Karten (ohne spezielle)
    Spezielle: TSpezielleArray;     //vorhandene spezielle Karten mit Name und
                                     //Index
  end;

  PTKartenSammlungInfo = ^TKartenSammlungInfo;

  //Definition der Funktion, die von einer Kartensammlung erwartet wird
  TDLLSammlungInfo = function(): TKartenSammlungInfo; stdcall;


procedure KopiereInfo(QuelleInfo: PTKartenSammlungInfo; var ZielInfo: TKartenSammlungInfo);
var
  b: Byte;
begin
  with ZielInfo do
  begin
    Name := QuelleInfo.Name;
    Version := QuelleInfo.Version;
    Karten := QuelleInfo.Karten;
    SetLength(Spezielle, Length(QuelleInfo.Spezielle)); //FEHLER
    for b := 1 to Length(QuelleInfo.Spezielle) do       //FEHLER
      Spezielle[b - 1] := QuelleInfo.Spezielle[b - 1];  //FEHLER
  end;
end;

function CheckSammlung(Sammlung: string): Boolean;
var
  KartenLib: THandle;                          //Handle auf die DLL
  DLLSammlungInfo: TDLLSammlungInfo;           //Addresse der Funktion
  Info: TKartenSammlungInfo;                   //Infos von der DLL
begin
  result := false;
  Sammlung := ExtractFilePath(ParamStr(0)) + PluginPfad + Sammlung;

  KartenLib := LoadLibrary(PChar(Sammlung));
  if KartenLib <> 0 then
  begin
    @DLLSammlungInfo := GetProcAddress(KartenLib, 'DLLSammlungInfo');
    if Assigned(DLLSammlungInfo) then
    begin
      KopiereInfo(@DLLSammlungInfo, Info);
      //Anzeigen der Informationen (mit zwei speziellen Karten)
      MessageBox(0, PChar('Name: ' + Info.Name + #10 +
                          'Version: ' + Info.Version + #10 +
                          'Kartenzahl: ' + IntToStr(Info.Karten) + #10 +
                          'Spezielle: ' + IntToStr(Length(Info.Spezielle)) + #10 +
                          'Sp1: ' + Info.Spezielle[0].Name + ' / ' + 
                                    IntToStr(Info.Spezielle[0].Index) + #10 +
                          'Sp2: ' + Info.Spezielle[1].Name + ' / ' + 
                                    IntToStr(Info.Spezielle[1].Index)
                          ), '', 0);}
    end;
  end;
  FreeLibrary(KartenLib);
end;
Das Problem: irgendo tritt eine AV auf. jetzt hab ich nach etwas debuggen rausgefunden, dass dieser beim übernehmen des Spezielle-Arrays auftritt. Allerdings kann ich die ursache für das problem nicht finden - liegt das am exportieren von solchen etwas komplexeren strukturen (-> anderer lösungsweg?) oder mache ich den fehler (-> wo ;-))

danke für hilfe,
heiopei

Sidorion 3. Mai 2006 11:40

Re: exportieren von Strukturen aus DLLs...
 
Du musst Result mit New erstellen.
Der Record, auf den Result zeigt, wird nur lokal erzeugt und beim Verlassen der Funktion wieder zerstört (analog zu Objektinstanzen, die Du ja auch kreieren musst, wenn Du sie als Funktionsergebnis verwenden willst).
Ausserdem solltest Du einen Pointer auf den Record zurückgeben.

himitsu 3. Mai 2006 11:55

Re: exportieren von Strukturen aus DLLs...
 
Das problem liegt erstmal in dem dynamischen Array, dadafür von der DLL Speicher reserviert wird, welcher (ohne einen SharedMemoryManager) nicht in anderen Modulen (also der EXE) verwendet werden kann (da der Speicher ja einem anderem MemoryManager gehört, kann der MemoryManager der EXE diesen nicht verwalten) und eventuell gibt's dann ja auch noch ein Problemchen mit der RTTI des dynamischen Arrays.

Außerdem kann es locker noch zu problemen bei der Recordausrichtung kommen, vorallem wenn der Compiler in der DLL anders ausrichtet, als in der EXE (würde dir daher in diesme Fall schonmal zu packed Record's raten)

heiopei 3. Mai 2006 11:59

Re: exportieren von Strukturen aus DLLs...
 
hallo,
hmm.... ok.

da soll mal einer draufkommen - die informationen werden nämlich richtig angezeigt, aber die av tritt erst auf, wenn ich versuche, das array zu übernehmen...

das mit dem pointer hab ich mir auch schon gedacht, aber nur zum teil verwirklicht :oops:


@himitsu: das mit dem packed array werd ich dann gleich mal testen (genauso wie New(result))

danke,
heiopei

himitsu 3. Mai 2006 12:07

Re: exportieren von Strukturen aus DLLs...
 
Zitat:

Zitat von heiopei
genauso wie New(result))

Du hast doch bestimmt keinem anderen MemoryManager (z.B. ShareMM, FastMM, oder ähnliches) eingebunden?

Der DelphiMM (wird standardmäßig installiert) wird für jedes Delphi-Modul einzeln erzeugt und diese verschiedenen MMs arbeiten nicht zusammen, daher kannst du keinen Speicherbereich in einem Modul (DLL/EXE) erstellen (egal ob mit New, SetLength, oder wie auch immer) und in dem anderen Modul freigeben, oder seine Größe ändern,
denn wenn du es versuchst, dann wird der entsprechende MemoryManager meckern (mit 'ner Exception reagieren), da er keine Referenz zu dem angegebenen Speicherbereich(Pointer) in seinen Verwaltungsdaten finden kann.


Zitat:

wenn ich versuche, das array zu übernehmen
wie gesagt ... lesen und schreiben kann man zwar in diesen Speicherbereich, da der ja zur Instance(Anwendung) gehört und somit alle Module dieser Instance zugriff haben, der Speicher kann außerhalb einenes Moduls nicht verwaltet werden.

also entweder du nimmst ein Array mit fester größe und übergibst es per Var-Parameter ... denn da wird der Speicher in deiner EXE reserviert und die DLL schreibt nur darain
Delphi-Quellcode:
type TSpezielle = record
    Name: PChar;
    Index: Word;
  end;
  TSpezielleArray = array[0..irgendwas] of TSpezielle;
  TKartenSammlungInfo = record
    Name: PChar;                    //Name der Kartensammlung
    Version: PChar;                 //Version der Kartensammlung
    Karten: Byte;                   //Anzahl der Karten (ohne spezielle)
    Spezielle: TSpezielleArray;     //vorhandene spezielle Karten mit Name und
                                     //Index
  end;

procedure DLLSammlungInfo(var Info: TKartenSammlungInfo); stdcall;
oder bei 'nem dynamischen array rufst du SetLength in der EXE auf und arbeitest auch per Var-Parameter
Delphi-Quellcode:
type TSpezielle = record
    Name: PChar;
    Index: Word;
  end;
  TSpezielleArray = array of TSpezielle;
  TKartenSammlungInfo = record
    Name: PChar;                    //Name der Kartensammlung
    Version: PChar;                 //Version der Kartensammlung
    Karten: Byte;                   //Anzahl der Karten (ohne spezielle)
    Spezielle: TSpezielleArray;     //vorhandene spezielle Karten mit Name und
                                     //Index
  end;

function DLLSammlungInfoSize: integer; stdcall;
procedure DLLSammlungInfo(var Info: TKartenSammlungInfo); stdcall;

// in der EXE dann
SetLength(Info.Spezielle, DLLSammlungInfoSize);
DLLSammlungInfo(Info);
und wenn es keine Probleme mit der RTTI geben sollte, dann kannst du es auch so lassen, wie bisher und bindest aber einen SharedMM ein, damit in der EXE und der DLL der die MemoryManager zusammen arbeiten und somit auch den Speicher des anderen MMs mit verwalten können.

heiopei 3. Mai 2006 12:12

Re: exportieren von Strukturen aus DLLs...
 
hallo himitsu,

aber ich rufe doch nur die funktion der DLL auf und weise das ergebnis einer eigenen variable zu, der ich die größe des array an das des ergebnisses anpasse (wasn satz:-)) - verändere ich da irgendwas in der DLL?hää???

PS: ich verwende nur den speichermanager von delphi (7).

himitsu 3. Mai 2006 12:20

Re: exportieren von Strukturen aus DLLs...
 
Ja.

aus der DLL
Delphi-Quellcode:
SetLength(Spezielle, 2);
du veränderst was an dem Array.

PS: hatte oben gerade noch was geändert gehabt.



Zitat:

PS: ich verwende nur den speichermanager von delphi (7).
kannst ja mal nach Hier im Forum suchenFastMM/Hier im Forum suchenFastXMM suchen

heiopei 3. Mai 2006 12:26

Re: exportieren von Strukturen aus DLLs...
 
jetzt hab ichs kapisch...

ok. das mit ner weiteren funktion, welche die größe des arrays ausgibt, hab ich mir auch überlegt, erschien mir aber erstmal überflüssig, weil ich ja nicht wusste, worans lag - nachher ist man immer schlauer :lol:

das mit anderen speichermanagern schau ich mir bei gelegenheit auch noch an, danke für den tipp.


mfg,
heiopei

heiopei 4. Mai 2006 13:30

Re: exportieren von Strukturen aus DLLs...
 
mittach,
so hab das jetzt mal alles umgesetzt, aber es tritt trotzdem noch eine av auf!.
(ich kann jetzt die infos erfolgreich übernehmen, aber bei anzeigen der info (strings) tritt wieder die av auf, bzw. nach der anzeige) :-(

der code der dll:

Delphi-Quellcode:
const
  SpezielleKarten = 2;

type
  TSpezielle = record
    Name: PChar;    
    ResID: Word;      
  end;

  TSpezielleArray = array [1..SpezielleKarten] of TSpezielle;

  TKartenSammlungInfo = record
    Name: PChar;      
    Version: PChar;  
    AnzKarten: Byte;        
    AnzSpezielle: Word;    
    Spezielle: TSpezielleArray;  
  end;

  PTKartenSammlungInfo = ^TKartenSammlungInfo;

function DLLSammlungInfo(): PTKartenSammlungInfo; stdcall;
begin
  with result^ do
  begin
    Name := 'Beispiel';
    Version := '1.0.0';
    AnzKarten := 32;
    AnzSpezielle := SpezielleKarten;
    Spezielle[1].Name := 'Speziell1';
    Spezielle[1].ResID := 65534;
    Spezielle[2].Name := 'Speziell2';
    Spezielle[2].ResID := 65535;
  end;
end;
das hauptprogramm:

Delphi-Quellcode:
type
  TSpezielle = record
    Name: PChar;    
    ResID: Word;      
  end;

  TSpezielleArray = array of TSpezielle;
  TKartenSammlungInfo = record
    Name: PChar;
    Version: PChar;
    AnzKarten: Byte;
    AnzSpezielle: Word;
    Spezielle: TSpezielleArray;
  end;

  PTKartenSammlungInfo = ^TKartenSammlungInfo;

  TDLLSammlungInfo = function(): PTKartenSammlungInfo; stdcall;


procedure KopiereInfo(QuelleInfo: PTKartenSammlungInfo; var ZielInfo: TKartenSammlungInfo);
var
  w: Word;
begin
  with ZielInfo do
  begin
    Name := QuelleInfo^.Name;
    Version := QuelleInfo^.Version;
    AnzKarten := QuelleInfo^.AnzKarten;
    AnzSpezielle := QuelleInfo^.AnzSpezielle;
    SetLength(Spezielle, AnzSpezielle);
    if AnzSpezielle > 0 then
      for w := 1 to AnzSpezielle do
        Spezielle[w - 1] := QuelleInfo^.Spezielle[w];
  end;
end;

function CheckSammlung(Sammlung: string): Boolean;
var
  KartenLib: THandle;                          //Handle auf die DLL
  DLLSammlungInfo: TDLLSammlungInfo;           //Addresse der Funktion
  Info: TKartenSammlungInfo;                   //Infos von der DLL
begin
  result := false;
  Sammlung := ExtractFilePath(ParamStr(0)) + PluginPfad + Sammlung;

  KartenLib := LoadLibrary(PChar(Sammlung));
  if KartenLib <> 0 then
  begin
    @DLLSammlungInfo := GetProcAddress(KartenLib, 'DLLSammlungInfo');
    if Assigned(DLLSammlungInfo) then
    begin
        KopiereInfo(DLLSammlungInfo, Info);
        DLLSammlungInfo := nil;
        MessageBox(0, PChar('Name: ' + Info.Name + #10 +
                            'Version: ' + Info.Version + #10 +
                            'Konvention: ' + Info.Konvention + #10 +
                            'Kartenzahl: ' + IntToStr(Info.AnzKarten) + #10 +
                            'Spezielle: ' + IntToStr(Info.AnzSpezielle) + #10 +
                            'Sp1: ' + Info.Spezielle[0].Name + ' / '
                                    + IntToStr(Info.Spezielle[0].ResID) + #10 +
                            'Sp2: ' + Info.Spezielle[1].Name + ' / '
                                    + IntToStr(Info.Spezielle[1].ResID)
                            ), '', 0);
       result := true;
    end;
  end;
  FreeLibrary(KartenLib);
end;
woran liegts jetzt wieder,
danke für tipps,

heiopei

ps: ich hab also in erster linie das dyn. array umgangen, in dem ich in der dll ein statisches erzeuge und die größe als weiteren info-paramter mitgebe!


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:49 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