![]() |
Objekte zw. Programm und DLL austauschen - Interfaces?
Hallo DP-User :hi:
ich schreibe gerade an einer kleinen Client/Server-Anwendung. Um das ganze möglichst flexibel zu halten, nutze ich ein selbstentwickeltes XML-ähnliches, binäres Format und habe an eine Plugin-Architektur gedacht. Dabei exportiere ich in der DLL eine Register-Funktion, die die Adressen aller in dieser DLL enthaltenen Funktionen mit Funktionsnamen in eine Liste im Hauptprogramm einträgt (ähnlich wie es Delphi mit BPLs macht). Wenn der Server einen Befehl bekommt, schaut er in seiner Liste nach und führt, wenn der Befehlsname in der Liste enthalten ist, das zugehörige Callback aus. Ich dachte daran, den Funktionen aus der DLL als Parameter einfach einen meiner "XML"-Tags zu übergeben, der wiederum andere Tags enthalten kann. So könnte ich die Parameterübergabe flexibel gestalten. KÖNNTE: Denn ich kann in der DLL nicht auf die Objekte aus dem Hauptprogramm zugreifen. Ich vermute, das liegt daran, dass DLL und Hauptprogramm jeweils ihre eigene RTL, VCL usw. haben. Ich vermute, dass ich dafür Interfaces benutzen muss. Mit denen habe ich aber noch nie gearbeitet. Helfen die denn überhaupt bei diesem Problem? Muss ich für alle bereits vorhandenen XML-Klassen, die ich in der DLL benutzen möchte, entsprechende Interfaces deklarieren? Das Problem ist nämlich, dass ich die Objekte nicht nachträglich von TInterfacedObject ableiten kann, weil sei teilweise schon von anderen Klassen abgeleitet sind. Heißt das wiederum, ich muss für jede Klasse eine Wrapperklasse konstruieren? Gibt es vielleicht Tools, die sowas automatisieren? Also am liebsten wäre es mir, wenn es eine andere Lösung gäbe... Im schlimmsten Falle könnte ich vielleicht den Inhalt der Tags in einen Buffer speichern und hinterher wieder einlesen :? |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Ja, Interfaces sind in diesem Zusammenhang durchaus recht nützlich. Sie werden nämlich nicht nach Adresse verglichen wie Klassen (was dort dazu führt, dass as und is fehlschlagen können, weil DLL.Klasse <> Hauptprogramm.Klasse), sondern nach GUID, welche natürlich überall gleich ist.
Dass du nicht von TInterfacedObject ableiten kannst, ist alles andere als tragisch. Die Implementierung der drei IInterface-Methoden ist äußerst kurz, du kannst sie einfach kopieren. Zur Frage, ob du zu jeder Klasse ein Interface deklarieren musst: Das Interface legt die Schnittstelle fest. Alles, was daher außerhalb des Moduls verwendet werden soll, sollte daher in einem Interface deklariert werden. |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Delphis XMLDocument ist bereits interface-basiert: IXmlDocument.
Was du beachten musst ist, dass die Lebensdauer von Interfaces durch Referenzzählung verwaltet wird. Wenn du einer Interface variable ein Objekt zuweist, dann wird dessen Zähler erhöht, aber wenn du aus den Scope rennst, dann wird er wieder um 1 reduziert. Erreicht er 0 wird die Instanz vernichtet. IOW: Wenn du ein Objekt überall als Klasse, nicht Interface, nutzt, dann könnte eine einmalige Zuweisung zu einer Interface instanz dieses Objekt zerstören wenn du die Methode verlässt. Ein Trick wäre den Referenzzähler künstlich um 1 zu erhöhen. (_AddRef) Wenn du es nur als Parameter übergibst, kannst du diesen auch als "const" deklarieren. Genau wie bei AnsiString wird dabei die Referenzzählung ignoriert. Hier passiert nix:
Delphi-Quellcode:
Das hier ist aber GANZ böse:
procedure Miep.DoSomething(const instance : ISomeInterface);
begin instance.SomeMethod(); end;
Delphi-Quellcode:
Wenn du in DoSomething ein Objekt wirfst, dann wird beim Zuweisen zu tmp der Referenzzähler auf 1 erhöht. Beim Verlassen der Methode aber reduziert und dadurch wird das Objekt zerstört.
procedure Miep.DoSomething(const instance : ISomeInterface);
var tmp : ISomeInterface; begin tmp : instance; end; Du kannst dem generell entgegenwirken, wenn du dafür sorgst, dass der Zäher immer mindestens 1 ist. Das erreichst du wenn du einmal _AddRef auf die Instanz ausführst. Die coolste Lösung ist gar keine Objektreferenzen von diesen Instanzen zu benutzen. Weder in der Exe noch in ener DLL. Dadurch funktioniert die Referenziertzählung überall und Objekte werden autom. freigegeben. :-) |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Danke für die Antworten
@Elvis: Wie gesagt, meine Klass eist nur XML-ähnlich aufgebaut von der Struktur her. Es ist aber ein eigenes, binäres Format. Ich habe mich gegen XML entschieden, weil die Kommunikation über das Internet laufen soll, und ich den Größenzuwachs bei XML insbersondere bei Binärdaten vermeiden will. Deshalb kann ich die Klasse von Delphi nicht benutzen. Wie kann ich denn völlig ohne Objektreferenzen arbeiten? Irgendwo muss das Objekt doch mal erzeugt werden :gruebel: @Apollonius: Ist es denn sinnvoll, den Code zu kopieren? Es sind zwar nicht sehr viele Klassen, aber irgendwie widerstrebt mir das. Zur Zeit sieht meine Deklaration so aus:
Delphi-Quellcode:
Es gibt noch ein TBinXMLDocument, das ich aber in der DLL nicht brauchen werde. Wie könnte man bei den obigen Deklarationen am sinnvollsten Interfaces einflicken?
TBinXMLValue = class(TMemoryStream)
private function getAsBoolean: boolean; function getAsFloat: extended; function getAsInt64: int64; function getAsLongint: longint; function getAsString: string; procedure setAsBoolean(const Value: boolean); procedure setAsFloat(const Value: extended); procedure setAsInt64(const Value: int64); procedure setAsLongInt(const Value: longint); procedure setAsString(Value: string); protected procedure SaveToStream(const aStream: TStream); procedure LoadFromStream(const aStream: TStream); public constructor Create; property AsString: string read getAsString write setAsString; property AsLongint: longint read getAsLongint write setAsLongInt; property AsInt64: int64 read getAsInt64 write setAsInt64; property AsFloat: extended read getAsFloat write setAsFloat; property AsBoolean: boolean read getAsBoolean write setAsBoolean; end; TBinXMLTag = class private FName: string; FChildren: TBinXMLTaglist; FParent: TBinXMLTag; FValue: TBinXMLValue; procedure SetName(const Value: string); protected procedure SetParent(const Value: TBinXMLTag); procedure SaveToStream(const aStream: TStream); procedure LoadFromStream(const aStream: TStream); public constructor Create(const aParent: TBinXMLTag = nil); destructor Destroy; override; property Name: string read FName write SetName; property Parent: TBinXMLTag read FParent; property Children: TBinXMLTaglist read FChildren; function NewChild(const aName: string=''): TBinXMLTag; property Value: TBinXMLValue read FValue; end; TBinXMLTagList = class(TObjectList) private FParent: TBinXMLTag; function GetItem(index: integer): TBinXMLTag; procedure SetItem(index: integer; const Value: TBinXMLTag); procedure FreeAllItems; function GetItemByName(Name: string): TBinXMLTag; protected procedure SetParent(const Value: TBinXMLTag); procedure SaveToStream(const aStream: TStream); procedure LoadFromStream(const aStream: TStream); public constructor Create(const aParent: TBinXMLTag = nil); property Parent: TBinXMLTag read FParent; property Items[index: integer]: TBinXMLTag read GetItem write SetItem; default; property ItemByName[Name: string]: TBinXMLTag read GetItemByName; function Add(AItem: TBinXMLTag): integer; end; |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
TInterfacedObject implementiert mit den drei Methoden die Referenzzählung und den as-Operator. Es gibt kaum andere sinnvolle Implementation (sieht man von Fällen ab, in denen man die Referenzzählung ausschalten will). Daher spricht eigentlich nichts gegen eine reine Kopie.
Bezüglich Objektreferenzen: Die Idee ist, dass du nirgendwo eine Variable vom Typ TBinXMLValue deklarierst, sondern immer nur IBinXMLValue. TBinXMLValue kommt damit nur noch im Ausdruck TBinXMLValue.Create vor. |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Zitat:
|
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Delphi macht das automatisch, z.B. wird in Prozeduren, bei denen eine lokale Variable ein Interface (oder ein langer String, dynamisches Array oder Variant) ist, automatisch ein Try-Finally-Block generiert, wodurch das _Release sichergestellt wird. Bei Klassen und Records läuft es ähnlich.
|
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Ja, aber TBinXMLValue wird ja z.B. in TBinXMLTag.Create erzeugt. Wenn ich jetzt statt TBinXMLValue IBinXMLValue benutzen würde, sähe das doch immer noch so aus:
Delphi-Quellcode:
Was schreibe ich dann in den Destruktor? Oder macht Delphi das auch automatisch?
constructor TBinXMLTag.Create;
begin // FValue = IBinXMLValue FValue := TBinXMLValue.Create; end; Danke für deine Hilfe |
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Ja, alle finalisierungsbedürftigen Felder sind in einer Tabelle in der Klassenstruktur gespeichert. CleanupInstance (welches von TObject.FreeInstance verwendet wird) ruft für alle Interfaces dann _Release auf.
|
Re: Objekte zw. Programm und DLL austauschen - Interfaces?
Zitat:
Hier mal ein Beistift, den ich mir jetzt nur im Browser aus den Fingern gesaugt habe. Du solltest die Meldung aus InnerMiep.DomeSothing sehen, und wenn du Test() verlässt, sollte "Futsch!" erscheinen.
Delphi-Quellcode:
type
ISomeInterface = interface ['...'] procedure DoSomething; stdcall; end; InnerMiep = class(TInterfaceObject, ISomeInterface) public procedure DoSomething; stdcall; destructor Destroy; override; end; Miep = class(TInterfaceObject, ISomeInterface) private fValue : ISomeInterface; public // diese property implementiert das Interface property Value : ISomeInterface implements ISomeInterface; procedure AfterConstruction; override; end; ... procedure Miep.AfterConstruction; begin inherited; fValue := InnerMiep.Create(); end; procedure InnerMiep.DoSomething; begin ShowMessage('Hmmm, don''t really know what exactly to do here...'); end; destructor InnerMiep.Destroy; begin ShowMessage('Futsch!'); inherited; end; ... procedure Test; var xyz : ISomeInterface; begin xyz := Miep.Create(); xyz.DoSomething(); end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:40 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-2025 by Thomas Breitkreuz