AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Plugin-System mit Interfaces und DLLs
Thema durchsuchen
Ansicht
Themen-Optionen

Plugin-System mit Interfaces und DLLs

Ein Thema von MasterEvil · begonnen am 21. Mär 2006 · letzter Beitrag vom 25. Apr 2006
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von MasterEvil
MasterEvil

Registriert seit: 12. Feb 2005
Ort: Hannover
234 Beiträge
 
Delphi 7 Enterprise
 
#11

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 16:28
also gar nicht freigeben? wird auch das Objekt selber entfernt am Ende?

greetz
Steffen
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#12

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 16:45
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.
Angehängte Dateien
Dateityp: zip interfacetest_164.zip (1,3 KB, 23x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#13

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 19:22
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:

procedure 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;
Man sieht das bei dem Aufruf mit CONST
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:
var
  MyIntf: IInterface;
// try <- unsichtbarer Code des Compilers
// Pointer(MyIntf) := nil;
begin
  MyIntf := TObjectClass.Create;
  MyIntf.Methode1();
end;
// finally <- unsichtbarer Code
// MyIntf._Release;
// end;
Man sieht auch hier wieder das ein Aufruf von ._AddRef() und ._Release() wiederum TÖDLICHE Konsequenzen haben wird.

Gruß Hagen
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#14

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 20:05
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
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#15

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 20:41
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:
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;
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.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#16

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 22:30
Man kann ein Objekt auch gemischt mit Interfaces benutzen, das geht dann so:

Delphi-Quellcode:
var
  Obj: TMyObject;
  Intf: IInterface;
begin
  Obj := TMyObject.Create;
  Intf := Obj;

  Intf.MethodA();
  Obj.MethodA();

end;
Wichtig ist nur:

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
  Mit Zitat antworten Zitat
Benutzerbild von mirage228
mirage228

Registriert seit: 23. Mär 2003
Ort: Münster
3.750 Beiträge
 
Delphi 2010 Professional
 
#17

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 22:36
Hi Hagen,

Ich glaube bei dir hat sich ein kleiner Schreibfehler eingeschlichen.
Bei der Dekleration der Funktion "_Self" hast Du den Ergebnistypen vergessen:
function _Self: TMyObject; //: TMyObject; fehlte mfG
mirage228
David F.

May the source be with you, stranger.
PHP Inspection Unit (Delphi-Unit zum Analysieren von PHP Code)
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#18

Re: Plugin-System mit Interfaces und DLLs

  Alt 23. Mär 2006, 22:41
Hallo,
Zitat von negaH:
Wichtig ist nur:

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.
Das ist noch nicht alles. Schau Dir mal mein Demoprogramm an. Da wird das Objekt schon durch den Aufruf einer Prozedur (mit dem Objekt als Parameter) freigegeben, weil eben die Referenzzählung durcheinendergerät.

Gruß
xaromz
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#19

Re: Plugin-System mit Interfaces und DLLs

  Alt 24. Mär 2006, 05:28
Zitat:
Das ist noch nicht alles. Schau Dir mal mein Demoprogramm an. Da wird das Objekt schon durch den Aufruf einer Prozedur (mit dem Objekt als Parameter) freigegeben, weil eben die Referenzzählung durcheinendergerät.
Nein, die Referenzzählung arbeitet einwandfrei, du machst einfach einen logischen Denkfehler.

Delphi-Quellcode:


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 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.

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:
  
// try
// Temp := MyObject; // RefCounter == +1
   TuWas(Temp);
// finally
// Temp._Release ; // RefCounter == 0, ergo MyObject.Free
// end;
Du siehst in jedem Falle würde nach dem Aufruf von TuWas() das Objekt immer fregegeben werden.
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:
type
  TForm1 = class
    FMyObject: TMyObject;
    FMyObjectIntf: IInterface;
  end;

procedure TForm1.FormCreate();
begin
  FMyObject := TMyObject.Create;
  FMyObjectIntf := FMyObject;
end;
Fertig ! Beim zerstören von TForm1 wird FMyObjectIntf automatisch auf nil gesetzt, ergo auch FMyObject.Free aufgerufen.

Gruß Hagen
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#20

Re: Plugin-System mit Interfaces und DLLs

  Alt 24. Mär 2006, 09:55
Hallo,
Zitat von negaH:
Nein, die Referenzzählung arbeitet einwandfrei, du machst einfach einen logischen Denkfehler.
Ich hab' da keinen Denkfehler, keine Angst. Erklärung folgt.
Zitat von negaH:
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.
Du benutzt aber ein anderes Beispiel als wir hier diskutieren. Bei uns sieht das so aus:
Delphi-Quellcode:
procedure Test1;
begin
  FMyObject := TMyObject.Create;
  TuWas(FMyObject);

  TuWasAnderes(FMyObjekt);
end;
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.
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 von negaH:
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.
Was übrigens das Gleiche macht, wie ich, nur implizit statt explizit.

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
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


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 07:27 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