![]() |
Re: Plugin-System mit Interfaces und DLLs
also gar nicht freigeben? wird auch das Objekt selber entfernt am Ende?
greetz Steffen |
Re: Plugin-System mit Interfaces und DLLs
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
das Zerstören des Objekts überlässt Du dem GC. Daher ruf ich auch _Release auf. Durch das manuelle hochzählen des Referenzzählers steht der am Ende auf Eins. Um das Objekt zu zerstören, rufst Du _Release auf, womit der Zähler auf Null geht und das Objekt sich selbst zerstört. Deshalb ist auch kein Free nötig. Ich häng mal ein Beispiel ran: Die beiden oberen Buttons erstellen ein Objekt, einmal mit _AddRef, einmal ohne. Der mittlere Knopf ruft eine Methode auf, der das Objekt als Interface übergeben wird. Der untere Knopf zerstört das Objekt mit _Release. Das Objekt gibt seinen Zustand mit einer MessageBox an. Ich hoffe, damit wird es etwas klarer. Gruß xaromz //Edit: @shmia: So wie ich das verstanden habe, will er ja das Objekt in seiner Hauptanwendung weiterverwenden, also nicht nur für eine einzige Prozedur erstellen. |
Re: Plugin-System mit Interfaces und DLLs
Der Aufruf von ._AddRef() und ._Release() ist eine sehr schlechte Idee und sollte eigentlich niemals notwendig sein.
Bei der Benutzung von Interfaces in Delphi ist ein Punkt ganz wichtig: Alle Zugriffe auf ein Objekt sollten immer über das Interface erfolgen. Eine manuelle Freigabe der Objekte hinter dem Interface oder des Interfaces selber ist niemals notwendig. Werden Interfaces als Paramater benutzt so sollte man diese als CONST deklarieren. Dies ist aber keine zwingende Notwendigkeit wenn man zb. die Variable selber in der Funktion noch weiter benutzen möchte. Der Unterschied zwischen CONST und nicht CONST ist nachfolgender:
Delphi-Quellcode:
Man sieht das bei dem Aufruf mit CONSTprocedure XYZ(MyIntf: IInterface); // try // MyIntf._AddRef; <- unsichtbarer Code durch den Compiler erzeugt begin MyIntf := nil; // zulässige Anweisung um das Interface "freizugebenen" innerhalb der lokalen Gültigkeit // MyIntf._Release; <- unsichtbarer Code durch Compiler MyIntf := ABC; // zulässig // MyInt._Release; <- usichtbarer Code // ABC._AddRef // MyIntf := ABC; MyIntf._Release; // TÖDLICH und absolut FALSCH, Variable MyIntf ist weiter <> nil und erzeugt AV im // letzten unsichtbaren try finally Block unten end; // finally <- unsichtbarer // MyIntf._Release; // end; procedure XYZ(const MyIntf: IInterface); // <- KEIN try finally durch compiler begin MyIntf := nil; // <- unzulässig da MyIntf ein Konstante MyIntf := ABC; // <- unzulässig MyIntf._Release; // eventuell TÖDLICH da das Interface danach einen Reference Counter von X -1 hat. Unter // Umständen wird das Objekt hinter dem Interface freigegeben // Es kann also auch erwünscht sein end; 1.) die Anwendung schneller wird weil der Compiler auf ein try finally Block verzeichten kann 2.) der Source SICHERER wird da man nicht unbeabsichtig wie im ersten Beispiel die Paramatervariable modifizieren kann Bei der Erzeugung von Interface geht man immer so vor
Delphi-Quellcode:
Man sieht auch hier wieder das ein Aufruf von ._AddRef() und ._Release() wiederum TÖDLICHE Konsequenzen haben wird.
var
MyIntf: IInterface; // try <- unsichtbarer Code des Compilers // Pointer(MyIntf) := nil; begin MyIntf := TObjectClass.Create; MyIntf.Methode1(); end; // finally <- unsichtbarer Code // MyIntf._Release; // end; Gruß Hagen |
Re: Plugin-System mit Interfaces und DLLs
Hallo Hagen,
mit Deinen Ausführungen hast Du natürlich recht. Weiter oben hat MasterEvil aber geschrieben, dass er im Hauptprogramm auf Methoden/Eigenschaften des Objektes zugreift, die nicht per Interface erreichbar sind. Das ist ja wegen der Sichtbarkeit vom "objektorientierten Standpunkt" aus auch gut so. Leider ist so ein Mix aber nicht wirklich gefahrlos möglich, da er zu den beschriebenen Seiteneffekten führt. Übrigens kann man manchmal auf die beiden Methoden _AddRef und _Release nicht verzichten. Mas muss nur wissen, was man tut (Ich musste mir auch mal meine eigene Implementierung der beiden Methoden schreiben, aber das ist 'ne andere Geschichte). Gruß xaromz |
Re: Plugin-System mit Interfaces und DLLs
Ich habe mir für ein Programm ein Klasse geschrieben, die erst auf die Interface-Referenzzählung "hört", wenn ich mittels einer eigenen Methode "Release" (unterschied zu _Release) als Freizugeben markiert habe. Damit kann ich das Objekt auch ohne Interface nutzen. Defekte Plugins zerschießen mir damit auch nicht das gesamte Programm.
Delphi-Quellcode:
Aber wenn man es richtig macht, dann sollte man besser ein weiteres Interface deklarieren, dass dann den Zugriff auf die "versteckten" Eigenschaften erlaubt. Dem Plugin-System muss man dieses Interface ja nicht verraten. Alternativ kann man auch eine GetObject: TObject Methode dem Interface hinzufügen, dass dann das dahinterliegende Objekt zurückliefert.
type
TInterfaceNeutralObject = class(TModifyObject) private FRefCount: Integer; FAllowRelease: Boolean; protected procedure CleanUp; virtual; // hier den Destruktorcode einfügen public destructor Destroy; override; function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; procedure Release; // Statt .Free muss Release aufgerufen werden end; { TInterfaceNeutralObject } function TInterfaceNeutralObject.QueryInterface(const IID: TGUID; out Obj): HRESULT; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; procedure TInterfaceNeutralObject.Release; begin if (FRefCount = 0) and not FAllowRelease then begin CleanUp; FAllowRelease := True; Free; end else begin CleanUp; FAllowRelease := True; if FRefCount = 0 then Free; end; end; procedure TInterfaceNeutralObject.CleanUp; begin end; function TInterfaceNeutralObject._AddRef: Integer; begin Result := -1; Inc(FRefCount); end; function TInterfaceNeutralObject._Release: Integer; begin Result := -1; Dec(FRefCount); if (FRefCount <= 0) and FAllowRelease then Free; end; destructor TInterfaceNeutralObject.Destroy; begin {$IFDEF INTFDEBUG} if not FAllowRelease then //raise Exception.CreateFmt('InterfaceNeutralObject "%s" was not destroyed by Release', [ClassName]); MessageBox(0, PChar(Format('InterfaceNeutralObject "%s" was not destroyed by Release', [ClassName])), 'Error', MB_ICONHAND or MB_OK); {$ENDIF INTFDEBUG} inherited Destroy; end; |
Re: Plugin-System mit Interfaces und DLLs
Man kann ein Objekt auch gemischt mit Interfaces benutzen, das geht dann so:
Delphi-Quellcode:
Wichtig ist nur:
var
Obj: TMyObject; Intf: IInterface; begin Obj := TMyObject.Create; Intf := Obj; Intf.MethodA(); Obj.MethodA(); end; sobald man eine Interface Variable mit dem einer Objektvariable zugewiesen hat wirkt das automatische Referencecounting der Interfaces und dem Delphi Compiler. D.h. die variable Obj muß und darf nicht mehr über .Free oder so freigegeben werden. Diese Ausführungen beziehen sich natürlich nur auf Klassen die auch eine Referenzzählung implementieren und diese auch benutzen. Man kann es auch so machen:
Delphi-Quellcode:
type IPublicInterface = interface {GUID} function MethodA: Integer; end; implementation type TMyObject = class; ISelf = interface {GUID} function _Self: TMyObject; end; TMyObject = class(TInterfacedObject, IPublicInterface, ISelf) function MethodA: Integer; function _Self; end; function TMyObject.MethodA: Integer; begin Result := 0; end; function TMyObject._Self: TMyObject; // Nichts, In EAX == versteckter 1. Parameter ist Self schon drinnen, und Result == EAX begin end; procedure Test; var Intf: IInterface; begin Intf := TMyObject.Create; (Intf as ISelf)._Self.MethodA; end; Gruß Hagen |
Re: Plugin-System mit Interfaces und DLLs
Hi Hagen,
Ich glaube bei dir hat sich ein kleiner Schreibfehler eingeschlichen. Bei der Dekleration der Funktion "_Self" hast Du den Ergebnistypen vergessen:
Delphi-Quellcode:
mfG
function _Self: TMyObject; //: TMyObject; fehlte
mirage228 |
Re: Plugin-System mit Interfaces und DLLs
Hallo,
Zitat:
Gruß xaromz |
Re: Plugin-System mit Interfaces und DLLs
Zitat:
Delphi-Quellcode:
Bei Test1 existiert defakto KEIN lokaler Gültigkeitsbereich für ein Interface. Ergo wenn man ein Object direkt einer Funktion übergibt die ein Interface erwartet und dieses NICHT als const deklariert hat, so wird das Objekt zerstört. Das ist auch logisch, denn betrachte mal den Fall das man TuWas() aufruft mit einem gerade frisch allozierten Interface das später im Program NICHT weiter benutzt wird. Würde der Compiler anderes arbeiten würde dieses allozierte Interface als Speicherleiche übrig bleiben.procedure TuWas(Intf: IInterface); // try // Intf._AddRef <- Referenzcounter ist jetzt +1 begin end; // finally // Intf._Release; <- Referenzcounter ist jetzt 0, ergo Object wird freigegeben // end; procedure Test1; begin TuWas(TMyObject.Create); // <- hier impliziter Aufruf, KEINE SICHTBARE Referenzzählung end; procedure Test2; var Intf: IInterface; begin Intf := TMyObject.Create; TuWas(Intf); end; Bei Test2 hat man nun explizit das erzeugte Object in eine Interfacevariable gespeichert. Man hat also damit auch explizit eine Scope=Gültigkeitsbereich der offensichtlich die Scope der lokalen Funktion ist, bzw. des lokalen Stacks. Der Compiler kann nun, weil WIR es IHM auch gesagt haben, das Refenerenzecounting benutzen. Es lässt sich nun streiten ob es nicht richtiger von Borland gewesen wäre für den Testfall in Test1 temporär eine "unsichtbare" lokale Interfacevariable anzulegen die dann gleich nach Rückkehr von TuWas auf nil gesetzt, ergo freigegeben wird. Das ändert nämlich nichts am Sachverhalt das diese Temporäre unsichtbare Interfacevariable wiederum inetwa so aussähe:
Delphi-Quellcode:
Du siehst in jedem Falle würde nach dem Aufruf von TuWas() das Objekt immer fregegeben werden.// try // Temp := MyObject; // RefCounter == +1 TuWas(Temp); // finally // Temp._Release ; // RefCounter == 0, ergo MyObject.Free // end; Das liegt einfach daran das der Compiler nicht das wissen kann was DU einfach mal so vorraussetzt. Nämnlich das FMyObject ein Feld deiner TForm Klasse ist und somit aus DEINER Sicht eine globale Gültigkeit besitzt. Demo ist aber tatsächlich nicht so, da FMyObject eine Klasse ist und kein Interface !! Eines ist also sicher, dieses Verhalten ist durchaus logisch, aber leider nicht so richtig dokumentiert. Fazit: wenn man mit Objekten und Interfaces arbeiten möchte dann sollte man immer mit Hilfe einer lokalen Interfacevariablen, in der man das Object speichert, arbeiten. Wenn DU also möchtest das dein FMyObject global zur kompletten laufzeit deines TForm auch gültig bleibt dann musst DU dies dem Compiler auch sagen. Inetwa so
Delphi-Quellcode:
Fertig ! Beim zerstören von TForm1 wird FMyObjectIntf automatisch auf nil gesetzt, ergo auch FMyObject.Free aufgerufen.
type
TForm1 = class FMyObject: TMyObject; FMyObjectIntf: IInterface; end; procedure TForm1.FormCreate(); begin FMyObject := TMyObject.Create; FMyObjectIntf := FMyObject; end; Gruß Hagen |
Re: Plugin-System mit Interfaces und DLLs
Hallo,
Zitat:
Zitat:
Delphi-Quellcode:
Hier knallts, weil eben der erste Aufruf den Referenzzähler erhöht und wieder vermindert, wodurch das Objekt freigegeben wird. Bei TuWasAnderes ist also das Objekt schon zerstört.
procedure Test1;
begin FMyObject := TMyObject.Create; TuWas(FMyObject); TuWasAnderes(FMyObjekt); end; Natürlich ist dieses Verhalten korrekt (was die Referenzzählung von Interfaces angeht), aber, und das ist der Punkt, unerwartet. Wer noch nicht so viel Ahnung von Interfaces hat, würde annehmen, dass FMyObjekt ja auch eine Refernez ist, aber auf das Objekt. Nur gibt's bei Objekten keinen Referenzzähler :( . Darum geht's mir, ich will nur auf diese Stolperfalle hinweisen. Und hier könnte der Delphi-Compiler tatsächlich erkennen, dass ich das Objekt nicht freigeben will und noch einen Referenzblock rumbasteln. Da Delphi das nicht macht, kann man es selbst machen. Zitat:
Aber wie wäre es eigentlich damit, und hier sind wir wieder beim Thema des Threads, wenn das Objekt zwei Interfaces implementiert, wobei nur das eine an das Plugin weitergegeben wird, während das andere im Hauptprogramm verwendet wird? So bräuchts man sich nicht mit diesem Problem rumschlagen. Gruß xaromz |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:29 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