![]() |
Übergabe von Listen an DLL ohne Kopie zu erstellen.
Hallo Kollegen,
Mein Problem ist wie folgt: Ich möchte Listen oder Inhalte von Komponenten an eine DLL übergeben. Soweit kein Problem, das funktioniert problemlos! Im Moment stehe ich total auf dem Schlauch. Ich weiß zwar, wie ich das in C# machen muss, kann das aber irgendwie nicht auf Delphi übertragen. Ich bin mir sicher, dass dies möglich ist (auch in Delphi) aber ich bekomm es nicht auf die Reihe. Hoffentlich drücke ich mich verständlich aus. Bei der Vorgehensweise die ich nun anstrebe, möchte ich aber innerhalb der DLL nicht mit einer Kopie arbeiten sondern mit dem Original. D.h. wenn ich die Liste/Komponente oder den Record oder was auch immer in der DLL berarbeite, möchte ich, dass das Ergebnis direkt in der Applikation sichtbar ist. Damit müsste ich dann nicht immer den lästigen Abgleich der Daten händisch vornehmen. Habt ihr irgendwelche Vorschläge, vielleicht auch mit einem kleinen Code-Schnipsel zum besseren Verständnis? ich sage schon mal vorab Danke. und by the way, euch allen ein gutes neues Jahr |
AW: Übergabe von Listen an DLL ohne Kopie zu erstellen.
Also ich hab das ganze über ne Klasse gelöst, die den Zugriff auf die Liste über interfaces kapselt. Um möglichst kompatibel mit verschiedenen Datentypen zu sein hab ich mir zuerst nen record definiert:
Delphi-Quellcode:
Dann baust du dir nen interface, das du an die DLL übergibst und den Zugriff auf die Listenelemente bereitstellt:
PDataModuleItem = ^TDataModuleItem;
TDataModuleItem = packed record Key : Pointer; Value : Pointer; datatype: word; Size : integer; end;
Delphi-Quellcode:
Das interface kann gleich noch nen interface besitzen, dass als Link fungiert und dem Besitzer mitteilt ob sich was geändert hat
IDataModule = interface(IEnumerator)
['{57AF81B3-B7A8-4B33-8174-C2DBA70DAC97}'] function GetLinked: boolean; stdcall; procedure GetLink(out Link); stdcall; procedure SetLink(const ALink: IInterface); stdcall; procedure RemoveLink; stdcall; function GetValueCount: integer; stdcall; function GetValue(index: integer): Pointer; stdcall; // liefert PDataModuleItem zurück procedure SetValue(index: integer; Value: Pointer); stdcall; procedure DeleteValue(index: integer); overload; stdcall; procedure DeleteValue(Key: PChar); overload; stdcall; function HasValue(Key: PChar): boolean; stdcall; procedure SetCharValue(AKey: PChar; AValue: PChar); stdcall; function GetCharValue(AKey: PChar): PChar; stdcall; procedure SetFloatValue(AKey: PChar; AValue: real); stdcall; function GetFloatValue(AKey: PChar): real; stdcall; procedure SetIntValue(AKey: PChar; AValue: Int64); stdcall; function GetIntValue(AKey: PChar): Int64; stdcall; procedure SetBoolValue(AKey: PChar; AValue: boolean); stdcall; function GetBoolValue(AKey: PChar): boolean; stdcall; property ValueCount: integer read GetValueCount; property Linked: boolean read GetLinked; end; Jetzt musst du dir nur noch ne Klasse definieren, die dieses Interface implementiert und schon kannst du das in die DLL rüberschicken bei Bedarf. Als Auszug könnte das so aussehen
Delphi-Quellcode:
FList is ne ThreadList, damit bist du auch noch Threadsafe, LinkNotification ist ne Methode des Datenmoduls, die bei Bedarf dem Link mitteilt dass sich was geändert hatfunction TCustomDataModule.GetCharValue(AKey: PChar): PChar; var index: integer; begin result := nil; index := IndexOf(AKey); with FList.LockList do try if (index > -1) and (PDataModuleItem(Items[index])^.datatype = STR_DATA) then result := PChar(PDataModuleItem(Items[index])^.Value); finally FList.UnlockList; end; end; procedure TCustomDataModule.SetCharValue(AKey: PChar; AValue: PChar); var Buffer: PDataModuleItem; begin Buffer := nil; // if IndexOf(AKey) > -1 then try Buffer := NewDataModuleItem(STR_DATA); Buffer^.Key := CopyPChar(AKey); Buffer^.Value:= CopyPChar(AValue); SetValue(IndexOf(AKey),Buffer); finally DisposeDataModuleItem(Buffer); end; end; procedure TCustomDataModule.SetValue(index: integer; Value: Pointer); var Buffer: PDataModuleItem; begin Buffer := nil; if (index > -1) and (index < ValueCount) then with FList.LockList do try Buffer := Items[index]; finally FList.UnlockList; end; if Assigned(Buffer) then begin LinkNotification(Buffer^.Key, Buffer^.datatype, lnsChanging); DisposeDataModuleItem(Buffer); end; Buffer := CopyDataModuleItem(PDataModuleItem(Value)); if Assigned(Buffer) then with FList.LockList do try if (index > -1) and (index < Count) then Items[index] := Buffer else Add(Buffer); LinkNotification(Buffer^.Key, Buffer^.datatype, lnsChanged); finally FList.UnlockList; end; end; Ach und ebenso gesundes neues Edit\\ CopyDataModuleItem kopiert das record, wobei du dann je nach pItem^.datatype die Werte sinnvoll kopieren solltest |
AW: Übergabe von Listen an DLL ohne Kopie zu erstellen.
Hi Sebastian,
erst mal Danke. Dein Beitrag ist riesig, will sagen ich bin erschlagen. Ich hatte da irgendwie auf etwas einfacheres gehofft. in C# gibt es da einfache Zuweisungen, die meist über einen Einzeiler zuzuweisen sind. Da war ich eben in der Hoffnung, dass man das irgenwie über xxx.Assign (yyy) oder so machen könnte. Da muss ich mich erst mal durchwursteln. Aber nochmal Danke |
AW: Übergabe von Listen an DLL ohne Kopie zu erstellen.
Hmm das ist nciht ganz so einfach in Delphi, da jede DLL für sich erstmal ihren eigenen Speichermanager, VCL... mitbringt. Das heisst wenn du ne Klasse in eine DLL rüberschieben willst, dann sieht die compilierte Klasse in der DLL mitunter ganz anders aus als im Hauptprogramm. Wenn du dann in die DLL eine Referenz auf ein Objekt aus dem Hauptprogramm übergibst, können schon einfache Aufrufe krachen. Daher in solchen Fällen immer interfaces benutzen.
Das gleiche gilt dann bei strings, die besitzen ne interne Referenzzählung, die du völlig über den Haufen wirfst, wenn du einen string in eine DLL übergibst, daher nehme ich PChars. Es gibt allerdings auch Ausnahmen. Da müsstes du mal nach Runtimepackages suchen, gegebenenfalls hilft dir auch ein anderer Speichermanager, wie zum Beispiel FastMM4 im einfachsten Fall, bei dem in der DLL weisst von welchem Datentyp die Einträge in der Liste sind, kannst du dir einfach ne Wrapper klasse bauen, der du im Create die Liste übergibst. Wenn diese Wrapperklasse nun ein interface unterstützt, mit dem du auf die die Elemente der Liste zugreifen kannst, dann geht das auch so. also in etwa:
Delphi-Quellcode:
EDIT\\ da kannst du dann im GetValue/SetValue auch gleich bei Änderungen die Anzeige updaten oder ähnliches...
PMyRecord = ^TMyRecord;
TMyRecord = packed record A: integer; B: boolean; end; IMyRecordWrapper = interface(IUnknown) procedure SetValue(AIndex: integer; ANewRecord: PMyRecord); stdcall; function GetValue(AIndex: integer): PMyRecord; stdcall; end; TWrapMyRecordList = class(TInterfacedObject, IMyRecordWrapper) private FMyList: TList; public constructor Create(AWrappedList: TList); reintroduce; virtual; procedure SetValue(AIndex: integer; ANewRecord: PMyRecord); stdcall; function GetValue(AIndex: integer): PMyRecord; stdcall; end; EDIT EDIT\\ und sry falls ich dich erschlagen hab, dachte mir wenn du sowas machen willst, dann hilft es dir vielleicht das ganze gleich so allgemein wie möglich zu halten, so dass du das später auch mal anderer Stelle verwenden kannst |
AW: Übergabe von Listen an DLL ohne Kopie zu erstellen.
Thx,
Zitat:
Wie gesagt, was mein Problem anbelangt bin ich da von C# verwöhnt und du weißt ja, wie das so ist, da kennt man was in der einen Sprache und versucht dann immer wieder verzweifelt ein Gutes aus der einen Sprache in einer anderen auch anzuwenden. Wenn mir Delphi aufgrund seiner Struktur nicht so ans Herz gewachsen wäre... Daher werde ich mich in den kommenden Tagen durch deine Snippets wühlen. Erklärt hast du ja gut, daher nochmal Danke. Bin aber weiterhin immer Dankbar für weitere Tipps in der Richtung. Könnte ja auch für andere interessant sein. Grüße |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:42 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