Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Dynamisches Array von DLL übergeben (https://www.delphipraxis.net/81845-dynamisches-array-von-dll-uebergeben.html)

Wormid 6. Dez 2006 16:45

Re: Dynamisches Array von DLL übergeben
 
Also, dann nochmal mein Beispiel von vorhin, mit Pointern...

Bei mir ist das kompilierbar und läuft ohne Exceptions durch. Ein Speichermanager muss weder in der DLL
noch in der Anwendung eingebunden werden. (Meine erste Version scheint nämlich nur bei statischem Linken
der Testfunktion einwandfrei zu funktionieren.)

Einmal die DLL...
Delphi-Quellcode:
library Project1;

type
  TTestArray = array of ShortString; // <--- ShortString !!!
  PTestArray = ^TTestArray;

function GetTestArray: PTestArray; stdcall;
var
  a: TTestArray;
begin
  SetLength(a, 3);
  a[0] := 'Hallo';
  a[1] := 'Welt';
  a[2] := '!';
  Result := PTestArray(a);
end;

exports
  GetTestArray;

begin
end.
Und hier die Unit1 von dem Testprojekt...
Delphi-Quellcode:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  TTestArray = array of ShortString;
  PTestArray = ^TTestArray;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  hDLL: Cardinal;
  GetTestArray: function: PTestArray; stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
  a: TTestArray;
  i: Integer;
begin
  hDLL := LoadLibrary(PChar('Project1.dll'));
  if hDLL <> 0 then
  begin
    GetTestArray := GetProcAddress(hDLL, 'GetTestArray');
    if GetTestArray <> nil then
    begin
      a := TTestArray(GetTestArray);
      ShowMessage(IntToStr(Length(a))); // Eine schicke "3" erscheint als Meldung
      for i := Low(a) to High(a) do
        ShowMessage(a[i]); // Hallo *click* Welt *click* ! *click*
      a := nil;
    end;
    FreeLibrary(hDLL);
  end;
end;

end.

TheMiller 6. Dez 2006 16:51

Re: Dynamisches Array von DLL übergeben
 
Danke für die Antworten. Habe mich aber erstmal für meine / Himitsu's Methode entschieden, da sie mir besser in mein noch auf Papier befindliches Konzept passt.

Vielen Dank - hat mir viel geholfen und hab wieder was gelernt!

ste_ett 6. Dez 2006 16:54

Re: Dynamisches Array von DLL übergeben
 
Zitat:

Zitat von Wormid
Also, dann nochmal mein Beispiel von vorhin, mit Pointern...

Bei mir ist das kompilierbar und läuft ohne Exceptions durch. Ein Speichermanager muss weder in der DLL
noch in der Anwendung eingebunden werden. (Meine erste Version scheint nämlich nur bei statischem Linken
der Testfunktion einwandfrei zu funktionieren.)

Das ist leider eine falsche Annahme. :)

Dadurch, dass du einen Pointerauf den Array als Rpckgabewert übergibst, wird jeglicher Speicher, der für den Array reserviert wurde wieder als beschreibbar markiert. Nur die vier Byte für den Pointer sind fest. Theoretisch könnte in der Millisekunde nach zurückgeben des Pointers eine andere Anwendung den Speicher, wo die drei Array-Elemente liegen, wieder nutzen.

Besser ist es, den Array als Parameter zu übergeben, in der DLL Speicher für den Arra zu allokieren und im Programm diesen Speicher wieder freizugeben, so gehen keine Informationen verloren. :)

Muetze1 6. Dez 2006 17:32

Re: Dynamisches Array von DLL übergeben
 
Wo ich hier gerade noch die neuen Beiträge sehe: Ich bezog mich auf ein Array mit Elemente die keine AnsiStrings beinhalten. Diese wären in dem Record/Array auch nur Referenzen und daher nicht mit dem Move()/CopyMemory kopierbar. Dazu kommt freilich noch, dass deren String-Inhalt vom Speichermanager der DLL alloziiert wurde und daher macht es dies noch komplizierter. Mein Vorschlag klappt nur unter den eben genannten Voraussetzungen und daher kann ich bei einem Array Of String oder Array Of Record mit AnsiStrings nicht helfen.

himitsu 6. Dez 2006 18:55

Re: Dynamisches Array von DLL übergeben
 
Zitat:

Zitat von Wormid
Delphi-Quellcode:
library Project1;

type
  TTestArray = array of ShortString; // <--- ShortString !!!
  PTestArray = ^TTestArray;

function GetTestArray: PTestArray; stdcall;
var
  a: TTestArray;
begin
  SetLength(a, 3);
  a[0] := 'Hallo';
  a[1] := 'Welt';
  a[2] := '!';
  Result := PTestArray(a);
end;

exports
  GetTestArray;

begin
end.

Zitat:

Zitat von ste_ett
Dadurch, dass du einen Pointerauf den Array als Rpckgabewert übergibst, wird jeglicher Speicher, der für den Array reserviert wurde wieder als beschreibbar markiert. Nur die vier Byte für den Pointer sind fest. Theoretisch könnte in der Millisekunde nach zurückgeben des Pointers eine andere Anwendung den Speicher, wo die drei Array-Elemente liegen, wieder nutzen.

Stimmt, was das mit dem Freigeben angeht ... man könnte Delphi ja auch daran hindern das Array automatisch freizugeben...
Delphi-Quellcode:
library Project1;

type
  TTestArray = array of ShortString; // <--- ShortString !!!
  PTestArray = ^TTestArray;

function GetTestArray: PTestArray; stdcall;
var
  a: TTestArray;
begin
  SetLength(a, 3);
  a[0] := 'Hallo';
  a[1] := 'Welt';
  a[2] := '!';
  Result := PTestArray(a);
  Pointer(a) := nil; // Array-Variable als Leer definieren
end;

procedure FreeTestArray(a: PTestArray); stdcall;
begin
  SetLength(TTestArray(a), 0);
end;

exports
  GetTestArray,
  FreeTestArray;

begin
end.

Theoretisch ist es aber auch möglich den Speicher(inhalt) komplett an den anderen MemoryManager zu übergeben (außer für Objekte und dergleichen).

Für den Fall des StringArrays gäbe es wohl 2 Hauptmöglichkeiten, entweder man kümmert sich selber um um die Verwaltung des Speichers (reservieren, freigeben, kopieren), oder man nutzt die String- und Arrayfunktionen für's kopieren.

Müßtest aber mal etwas mehr über deine Funktion sagen.
- entwerder die gesammte funktion zeigen
- oder zumindestens deren definition und ob die übergebenen parameter intern verändert werden.




So als Beispiel, wie es eventuell gehen könnte:
Delphi-Quellcode:
Type TExtGetMem = Function(i: Integer): Pointer;

Var ExtGetMem: TExtGetMem;

Procedure SetExtGetMem(G: TExtGetMem);
  Begin
    ExtGetMem := G;
  End;

Function Explode(S, Sep: String): TStringArray;
  Var Temp: PStringArray;
    P: PChar;
    i: Integer;

  Begin
    ...

    // Speicher übergeben ...
    If Result = nil Then Exit;
    Temp := PChar(GetMemory(Length(Result) * 4) + 8) + 8;
    PInteger(Integer(Temp) - 8)^ := 1;              // Array.RefCount
    PInteger(Integer(Temp) - 4)^ := Length(Result); // Array.ElementCount
    P := Temp;
    For i := 0 to High(Result) do Begin
      If Result[i] <> '' Then Begin
        PPointer(P)^ := PChar(GetMemory(Length(Result) * 4) + 9) + 8; // Array.Data[i]
        Move((P - 8)^, PChar(Result[i]) - 8, Length(Result[i] + 9);
      End Else PPointer(P)^ := nil;
      Inc(P, 4);
    End;
    Result := nil;        // Array leeren
    Pointer(Result) := P; // ArrayDaten des anderen MM zuweisen
  End;

Export
  SetExtGetMem,
  Explode;
Ich hoff i hab's soweit richtig ... war jetzt nur aus'm Kopf und ohne Delphi, also nicht getestet.
Sieht zwar schwerer aus, als es ist, aber im Grunde mußt du nur jeden einzelnen Speicherblock in den anderen MemoryManager kopieren und die Pointer entsprechend anpassen. :roll:

Bist du dir also sicher, daß du keinen SharedMM verwenden willst?

ach ja, aufgerufen würde es dann so:
Delphi-Quellcode:
SetExtGetMem(@GetMemory);
...
myStringArray := Explode(S, Sep);
Dat war also die Methode mit dem selber drum kümmern, wobei hier alles in der Methode drin ist, für's Andere geb ich vielleich etwas später noch ä kurzes Beispiel.

himitsu 6. Dez 2006 19:52

Re: Dynamisches Array von DLL übergeben
 
So, hier noch 'ne Variante wo die String/Arrrayfunktionen genutzt werden, es ist also "keine" Kenntnis über den inneren Aufbau nötig.

Dafür ist aber beim Aufrufen der Funktion etwas mehr von nöten, da hierfür natürlich die Funktionen der Exe verwendet werden müssen.
Wobei man natürlich auch alle nötigen String-/Arrayfunktionen bei der DLL regiestrieren kann und dann diese vorn dort aus aufruft, wobei dann der Code in der EXE in etwa dem vorherigen Beispiel entspricht.
Delphi-Quellcode:
Var myStringArray, Temp: TStringArray;
  i: Integer;

Temp := Explode(S, Sep);
SetLength(myStringArray, Length(Temp));
For i := 0 to High(Temp) do Begin
  myStringArray[i] := Temp[i];
  UniqueString(myStringArray[i]);
End;
FreeArray(Temp);




// in der DLL

Procedure FreeArray(Var a: TStringArray);
  Begin
    A := nil;
  End;

Function Explode(S, Sep: String): TStringArray;
  ...

Eichhoernchen 6. Dez 2006 20:10

Re: Dynamisches Array von DLL übergeben
 
Ich hab auch mal was ausprobiert und es klappt auch:

Delphi-Quellcode:

//Programm, dass die DLL benutzt

type
  TArray = array of string;

function GimmeAnArray(str: string): TArray;
type
  TMeineDllFunk = function (str: PChar): pointer;
  PArray = ^TArray;
var
  hinst: cardinal;
  func: TMeineDllFunk;
  arrlen, i: integer;
  arr: PArray;
begin
  hinst := LoadLibrary('arraydll.dll');
  try
    if hinst > 0 then
    begin
      @func := GetProcAddress(hinst, 'GimmeAnArray');
      if @func <> NIL then
      begin
        arr := func(PChar(str));
        setlength(result, length(arr^));
        for i := 0 to high(result) do
        begin
          result[i] := Arr^[i];
          UniqueString(result[i]);
        end;
        arr := nil;
      end;
    end;
  except
    FreeLibrary(hinst);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  arr: TArray;
begin
  arr := GimmeAnArray('HUHU');
  for i := 0 to high(arr) do
    Memo1.Lines.Add(arr[i])
end;
Delphi-Quellcode:
//Die DLL

var
  arr: array of string;


function GimmeAnArray(str: PChar): integer;
var
  i: integer;
  data: string;
begin
  data := str;
  setlength(arr, length(str));
  for i := 0 to high(arr) do
    arr[i] := data[i+1];
  result := Integer(@arr);
end;

exports
  GimmeAnArray;

begin
end.

Ich weiß jetzt nicht, welche Version von den geposteten hier besser ist, diese Funktioniert auf jeden Fall mit Pointern.

TheMiller 6. Dez 2006 20:21

Re: Dynamisches Array von DLL übergeben
 
So, integer-Arrays etc kann ich jetzt super übergeben. Doch mit Strings habe ich meine Probleme. Mache es so, wie mit den Integern nur frage ich erst nach der Länge der Strings:

Delphi-Quellcode:
function createArray: pointer; stdcall;
var
  StringArray: Array of String;
begin
  SetLength(StringArray, 2);
  StringArray[0]:='11';
  StringArray[1]:='32';
  result:=pointer(StringArray);
end;

function getElementByIndex(Buffer: PChar; BufferLen, Index: Integer; p: Pointer;): Integer; stdcall;
var
  StringArray: TStringArray;
  Element: String;
begin
  StringArray:=TStringArray(p);
  Element:=IntArray[index];

  if Assigned(Buffer) then
    StrLCopy(Buffer, PChar(Element), BufferLen);

  result:=Length(High)
  pointer(StringArray):=nil;
end;
In der Hauptanwendung sieht der Code so aus:

Delphi-Quellcode:
type
  TStringArray = array of String;

...

procedure TForm2.GetArray;
var
  len: Integer;
  Buffer: PChar;
  zeiger: Pointer;
  StringArray: TStringArray;
  s: String;
begin
  zeiger:=TStringArray(createArray);
  StringArray:=zeiger;

  len:=getArrayElement(nil, 0, 1, zeiger);
  try
    GetMem(Buffer, len+1);
    len:=getArrayElement(Buffer, len+1, 1, zeiger);
    s:=String(Buffer);
  finally
    FreeMem(Buffer);
  end;

  ShowMessage(s);
end;
Das Resultat ist einfach ein leerer String. Aber keine A/V - Auch nicht beim beenden des Programms...

Danke im Voraus

TheMiller 7. Dez 2006 13:18

Re: Dynamisches Array von DLL übergeben
 
Hallo,

jetzt hab ich noch ein kleines Problem. Es funktioniert soweit alles. Nur jetzt kommt beim Auselen der Strings aus dem Array eine A/V, aber NUR(!) dann, wenn ich den Index mit der Schleifenvariablen definiere. Trage ich 0, 1 etc ein klappt das wunderbar. Es liegt aber definitiv nicht daran, dass die Variable höher zählt, als das Array groß ist. Das ist nicht der Fall. Hier nochmal mein Code:

Delphi-Quellcode:
  Data:=TStringArray(createArray);
  zeiger:=Data;

  for i:=0 to getArrayCount(zeiger) do
  begin
    len:=getElement(Buffer, i, 0, zeiger);
    try
      GetMem(Buffer, len+1);
      len:=getElement(Buffer, i, len+1, zeiger);
      s:=String(Buffer);
    finally
      FreeMem(Buffer);
      Buffer:=nil;
      len:=0;
    end;
    ShowMessage(s);
  end;
Alle Arrays wurden in der DLL mit

Delphi-Quellcode:
myArray(zeiger):=nil;
Freigegeben.

Danke nochmals!

TheMiller 7. Dez 2006 17:34

Re: Dynamisches Array von DLL übergeben
 
Habe ich hier was nicht richtig freigegeben? In einem neuen Programm geht's nämlich. Er häbgt sich beim 3 Auslesen aus einer INI was auf. Macht er ohne den Aufruf der Array-DLL-Funktionen nicht. Was kann dafür der Grund sein?

Sehr merkwürdig!


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:47 Uhr.
Seite 3 von 4     123 4      

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