AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Dynamisches Array von DLL übergeben

Ein Thema von TheMiller · begonnen am 4. Dez 2006 · letzter Beitrag vom 8. Dez 2006
Antwort Antwort
Seite 3 von 4     123 4      
Benutzerbild von Wormid
Wormid

Registriert seit: 26. Aug 2003
Ort: Steinfurt
292 Beiträge
 
Delphi XE2 Professional
 
#21

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 15:45
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.
Debuggers don't remove Bugs, they only show them in Slow-Motion.
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#22

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 15:51
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!
  Mit Zitat antworten Zitat
Benutzerbild von ste_ett
ste_ett

Registriert seit: 10. Sep 2004
Ort: Dülmen
464 Beiträge
 
Delphi 7 Professional
 
#23

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 15:54
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.
Stefan
"Geht nicht!" ist keine Fehlerbeschreibung und "Hab ich schon versucht!" keine Antwort!

Hey, it compiles! Ship it!
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#24

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 16:32
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.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.170 Beiträge
 
Delphi 12 Athens
 
#25

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 17:55
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 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.

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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.170 Beiträge
 
Delphi 12 Athens
 
#26

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 18:52
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;
  ...
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Eichhoernchen

Registriert seit: 22. Apr 2004
Ort: Hagen
322 Beiträge
 
Turbo Delphi für Win32
 
#27

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 19:10
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.
Jan
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#28

Re: Dynamisches Array von DLL übergeben

  Alt 6. Dez 2006, 19:21
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
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#29

Re: Dynamisches Array von DLL übergeben

  Alt 7. Dez 2006, 12:18
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

myArray(zeiger):=nil; Freigegeben.

Danke nochmals!
  Mit Zitat antworten Zitat
Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#30

Re: Dynamisches Array von DLL übergeben

  Alt 7. Dez 2006, 16:34
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!
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 4     123 4      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:43 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