![]() |
Dynamisches Array von DLL übergeben
Hallo,
habe schon seit längerem ein kleines Problem. Ich habe eine Funktion und weiß nicht, wie ich ein Array aus einer DLL-Funktion an die Hauptapplikation übergeben kann. Dabei benutze ich nicht den Delphi-Speichermanager, sondern versuche das so zu regeln, was bei den Array auch nicht klappt - sonst schon! danke Leute!! |
Re: Dynamisches Array von DLL übergeben
Zitat:
Lass doch den Client den Speicher alloziieren. |
Re: Dynamisches Array von DLL übergeben
Zeig mal ein bischen Code was du sonst so machst und wie du es beim Array machen willst
|
Re: Dynamisches Array von DLL übergeben
Habe in der DLL eine Funktion (meine eigene Explode-Funktion). Da geteilten Strings möchte ich dann in ein Array packen und von da aus an die Hauptanwendung schicken. Meine Explode-funktion (auszug):
Delphi-Quellcode:
Hatte vorher den String "s" übergeben und das hat alles supeer funktioniert. Nur ich weiß nicht, wie ich die Speicheranforderung per Array machen soll. So hatte ich den Speicher für den String "s" angefordert (Anwendung)
for i:=0 to sl.Count-1 do
begin for j:=StrToInt(sl2.Strings[i]) to StrToInt(sl.Strings[i])-1 do begin s:=s+p[j]; end; SetLength(Content, i); content[i]:=s; end; .... if Assigned(Buffer) then begin StrLCopy(Buffer, PChar(content), BufferLen); end; result:=length(s);
Delphi-Quellcode:
Danke!
len:=Explode(nil, PChar(Source), PChar(Separator), 0);
try GetMem(Buffer, len+1); len:=Explode(Buffer, PChar(Source), PChar(Separator), len+1); s:=String(Buffer); finally FreeMem(Buffer); end; |
Re: Dynamisches Array von DLL übergeben
Dynamische Arrays kannst du mit setLength vergrößern/verklenern (Speicher anfordern/freigeben)
|
Re: Dynamisches Array von DLL übergeben
Ja schon klar. Nur wie kann ich das DynArray als Rückgabewert einer Funktion aus einer DLL in die Hauptanwendung nutzen?
|
Re: Dynamisches Array von DLL übergeben
Ich denke ohne gemeinsamen Speichermanager ist das AFAIK nicht sicher möglich.
Ansonsten mußt du mit C-Like Arrays und direkte Speicheranforderung/Freigabe von Windows arbeiten. |
Re: Dynamisches Array von DLL übergeben
hm...
oder ich muss den schritt halt aufteilen: Die Funktion gibt mir lange Strings zurück, die ich in der Hauptanwendung in diese dann dort in Array packe. Finde ich zwar nicht so schön, aber so solls halt erstmal sein. Danke euch. Aber wenn's noch Vorschläge gibt, nehm ich sie gerne an! |
Re: Dynamisches Array von DLL übergeben
Das hilft dir jetzt sicherlich nicht direkt weiter, aber ich hab genau für den Fall heute etwas in der Delphi Hilfe gesehen! Nur weiss ich leider nicht mehr wo :roll:
|
Re: Dynamisches Array von DLL übergeben
Hm...
das ist gut und schlecht. Ich hab grad gesehen. Brauche es echt dringend. Nur wenn du nicht mehr weißt wo, dann hilft mir das wirklich nicht. Brauch aber die Lösung... |
Re: Dynamisches Array von DLL übergeben
Zitat:
Würde das nicht irgendwie dem Sinn der DLL widersprechen? Ich meine du zerteilst erst etwas mit deiner Explode Funktion und dann bastelst du wieder einen String, den man um ihn in ein Array zu packen wieder mit einer Explode-artigen Funktion bearbeiten muss um das Ergebnis in ein Array zu speichern. Hört sich für mich Sinnlos an. Kannst du nicht vielleicht einen Pointer übergeben, der auf eine Speicheradresse mit dem Array zeigt? Aber das würde wahrscheinlich nur bei konstanter Größe gehen richtig? |
Re: Dynamisches Array von DLL übergeben
Pointer sind schon der richtige Weg - denke ich.
Mein Problem ist folgendes. Ich möchte einen Datensatz aus einer DB per PHP laden. Da habe ich dann verschiedene Felder (klar -> ist ja eine DB) diese Felder bzw. deren Werte sind durch einen Separator getrennt. Das Ende des Datensatzes hat einen anderen Separator. Jetzt möchte ich alle Datensätze in ein Record speichern. Dazu muss ich doch erst die Werte zurückgeben lassen (die "Sep1" getrennt) und das Ende eines Datensatzes finden ("Sep2"). Jetzt muss ich erst nach Sep2 exploden und diese in ein Array speichern, sodass ich auf die einzelnen Datensätze zugreifen kann. Dann kann ich mich an die Werte machen ("Sep1") und kann diese Daten dann schön in mein Array speichern. Soweit mein Vorhaben *g* |
Re: Dynamisches Array von DLL übergeben
Mit Pointer funktioniert das so nicht!
Das Arbeiten mit dynamischen Arrays zwischen DLL und Applikation ist, wie ich schmerzlich lernen mußte, nicht ohne weiteres möglich. Besonders dann nicht, wenn du ohne Speichermanager arbeiten willst. Ich habe mir für diesen Fall eine "Krücke" gebastelt, in dem ich das Array in der DLL verwalten lasse und mir Export-Funktionen gebaut habe, die mir u.a. die Länge des DLL-Arrays zurück geben. Die einzelnen Array-Elemente lasse ich mir mit einer weiteren Export-Funktion(Index):PChar zurück geben. Hierbei muss ich nur dafür sorgen, das ausreichend Speicher reserviert wird, sonst bekommt man nur ein Teil des String wieder zurück. Quasi überträgt man nicht das Array komplett, sondern nur die einzelnen Elemente des Arrays. Wenn du auf Applikationsseite die einzelen Elemente wieder in ein Array speicherst, dagegen spricht nix. Es ist halt nur nicht möglich das Array komplett per Parameter wieder zurück zubekommen. Zumindest nicht ohne Speichermanager. Sorry, mit Beispielcode kann ich dir augenblicklich nicht dienen. |
Re: Dynamisches Array von DLL übergeben
Frage an gmc616: Warum gibt die DLL nicht einfach zurück, wieviele Einträge es in dem Array gibt und eine weitere Funktion gibt die Adresse des ersten Eintrags an. Dies würde der Applikation doch schon reichen, schliesslich sind alle Arrays im Speicher fortlaufend und somit kann sie sich mit einem grossen CopyMemory() oder Move() Befehl den kompletten Array Inhalt kopieren ohne über jedes einzelne Element zu laufen.
|
DP-Maintenance
Dieses Thema wurde von "Daniel" von "Programmieren allgemein" nach "Sonstige Fragen zu Delphi" verschoben.
|
Re: Dynamisches Array von DLL übergeben
Hallo Muetze1,
Das habe ich schon probiert. Ich lade meine DLL dynamisch. Das Kopieren per CopyMemory funktioniert auch ohne Probleme. Wenn Ich allerdings die DLL mit FreeLibrary wieder entlade, bekomme ich eine Schutzverletzung bei Schreiben(!) an Adresse xy. Vlt. liegt es daran, dass ich mit Array of Records arbeite. Vlt. muß ich auch irdendwo wieder Speicher freigeben, wüsste aber nicht welchen. Oder es liegt daran, das es ohne Speichermanager einfach nicht funktioniert. Ich weiß es nicht. Daher die Krücke. Es wäre allerdings ne feine Sache, wenn das so einfach mit CopyMemory funktioniert täte / tun würde. :hi: gmc |
Re: Dynamisches Array von DLL übergeben
Bekomme ich hier was nicht mit? :gruebel:
Man kann doch dynamische Arrays aus einer DLL zurückgeben...
Delphi-Quellcode:
library xyz;
uses ... type TTestArray = array of ShortString; function GetTestArray: TTestArray; stdcall; begin SetLength(Result, 3); Result[0] := 'Hallo'; Result[1] := 'Welt'; Result[2] := '!'; end; exports GetTestArray; end.
Delphi-Quellcode:
*Edit: "Testprogramm" um dynamisches Einbinden der Funktion erweitert
program test_xyz;
interface type TTestArray = array of ShortString; ... var GetTestArray: function: TTestArray; stdcall; implementation procedure TestItOut; var a: TTestArray; i: Integer; hDLL: THandle; begin hDLL := LoadLibrary('xyz.dll'); if hDLL <> 0 then begin GetTestArray := GetProcAddress(hDLL, 'GetTestArray'); if GetTestArray <> nil then begin a := 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* SetLength(a, 0); GetTestArray := nil; end; FreeLibrary(hDLL); end; end; |
Re: Dynamisches Array von DLL übergeben
Zitat:
Zitat:
Ansonsten Speicher des Array freigeben? Naja, SetLength(Array, 0); - aber eigentlich nicht unbedingt von nöten. Ich werde die Tage selber mal ein Beispielprojekt dazu aufbauen und das ganze nochmal im Detail versuchen nach zu vollziehen. |
Re: Dynamisches Array von DLL übergeben
Hm...
hab mir dazu nochmal gedanken gemacht und getestet (hab mit Pointern noch nie was gemacht - lerne aber gerne dazu) und einiges rumprobiert. hab zum Testen die Funktion myArray, die mir den zeiger zurückgeben soll. Eine weitere Funktion empfängt einen Zeiger und weist dementsprechend den Wert aus. Mein Programm fragt also nach einem Zeiger, empfängt ihn, sendet ihn an Funktion 2 und empfängt den String. Vom Prinzip her garnicht mal so übel (hoffe ich). Doch ich denke, mein erster Fehler liegt schon in der ersten Funktion, da ich nicht weiß, welchen Rückgabewert ich benutzen muss. Ich denke, da Integer und Pointer so ziemlich gleich sind, kann ich den Pointer in einen Integer packen, gibt aber bei der Rückgabe im Programm nur Schrott aus. Weis nicht, wie ich auf einen Pointer zugreifen kann, um dessen Wert zu bekommen.:
Delphi-Quellcode:
Danke nochmal!
function myArray: integer;
var testarr: array of byte; zeiger: ^integer; begin SetLength(testarr, 2); randomize; testarr[0]:=random(9999); testarr[1]:=32; zeiger:=@testarr[0]; result:=Integer(zeiger); end; |
Re: Dynamisches Array von DLL übergeben
Da die Array-Variable selber ein Pointer ist, kannst du auch direkt casten:
('s kommt aber darauf an, wie du es drüben verwenden willst)
Delphi-Quellcode:
oder etwas schöner ... du gibst ja einen Zeiger zurück :zwinker:
function myArray: integer;
var testarr: array of byte; begin SetLength(testarr, 2); randomize; testarr[0]:=random(9999); testarr[1]:=32; result:=Integer(testarr); end;
Delphi-Quellcode:
und zurück dann z.B. so:
function myArray: pointer;
var testarr: array of byte; begin SetLength(testarr, 2); randomize; testarr[0]:=random(9999); testarr[1]:=32; result:=pointer(testarr); end; type Ttestarr = array of byte; Ttestarr(myArray) PS: randomize; nicht ständig aufrufen! PSS: SetLength(Arr, 0); dat is schneller, da es direkt ClearArray aufruft ._. Arr := nil; |
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:
Und hier die Unit1 von dem Testprojekt...
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.
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. |
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! |
Re: Dynamisches Array von DLL übergeben
Zitat:
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. :) |
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.
|
Re: Dynamisches Array von DLL übergeben
Zitat:
Zitat:
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:
Ich hoff i hab's soweit richtig ... war jetzt nur aus'm Kopf und ohne Delphi, also nicht getestet.
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; 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:
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.
SetExtGetMem(@GetMemory);
... myStringArray := Explode(S, Sep); |
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; ... |
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. |
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:
In der Hauptanwendung sieht der Code so aus:
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;
Delphi-Quellcode:
Das Resultat ist einfach ein leerer String. Aber keine A/V - Auch nicht beim beenden des Programms...
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; Danke im Voraus |
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:
Alle Arrays wurden in der DLL mit
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;
Delphi-Quellcode:
Freigegeben.
myArray(zeiger):=nil;
Danke nochmals! |
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! |
Re: Dynamisches Array von DLL übergeben
Also hier die Lösung (habs ganz alleine rausgefunden ;-) )
Delphi-Quellcode:
Bitteschön
Data:=TStringArray(createArray);
zeiger:=Data; for i:=0 to getArrayCount(zeiger) do begin len:=getElement(0, i, 0, zeiger); //hatte vorher "Buffer" im 1. Parameter stehen. Ist falsch! 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; und Dankeschön! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:55 Uhr. |
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