[gelöst] Interface als published property geht nicht

Rund um die LCL und andere Komponenten
Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Interface als published property geht nicht

Beitrag von af0815 »

Ich teste gerade mit aktuellen fpc trunk und Lazarus trunk, die habe ich komplett auf einer persistenten Ramdisk. Wenn es funktioniert teste ich es später auf stabilen fpc für Lazarus fixes 1.8.x. Aktuell sind also die letzten Erkenntnisse auf trunk/trunk basierend.

Generell halte ich das Problem komplett Lazarus sitzend. Beim FPC habe ich die entsprechenden Tests gefunden und dort ist meiner Meinung nach alles dafür vorhanden und auch eingebunden. Was ich sehe im Code des ObjectInpektors von Lazarus, das die Verwendung der Interfaces gar nicht richtig/komplett vorbereitet sind.

Ein Problem ist, das ein Interface nicht von TObject (und Nachfahren abstammt).

Code: Alles auswählen

Index: components/ideintf/objectinspector.pp
===================================================================
--- components/ideintf/objectinspector.pp   (revision 56931)
+++ components/ideintf/objectinspector.pp   (working copy)
@@ -2204,7 +2204,10 @@
   if (not (paSubProperties in Row.Editor.GetAttributes)) then exit;
   // check if circling
   if (Row.Editor is TPersistentPropertyEditor) then begin
-    AnObject:=TPersistent(Row.Editor.GetObjectValue);
+    if (Row.Editor is TInterfacePropertyEditor) then
+      AnObject:=TPersistent(Row.Editor.GetIntfValue)
+    else
+      AnObject:=TPersistent(Row.Editor.GetObjectValue);
     if FSelection.IndexOf(AnObject)>=0 then exit;
     ParentRow:=Row.Parent;
     while ParentRow<>nil do begin
 

es ist mir durch die Warnungen beim Kompileren des obigen Teil des Patches aufgefallen. Der erzwungene Cast AnObject:=TPersistent(Row.Editor.GetIntfValue) ist genaugenommen nicht erlaubt, da TPersistent kein Nachfahre von TInterface ist.

Ich habe noch nicht wirklich eine Idee wie das alles zusammenhängen kann. Das Problem tritt aber erst auf, wenn man wie im Bugrport beschrieben die Zuweisung macht. Nicht vorher.

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Interface als published property geht nicht

Beitrag von Michl »

Ja das aufräumen funktioniert noch nicht richtig. Sehe ich auch:

Code: Alles auswählen

#0 fpc_intf_decr_ref(0x153f64f8) at ..\inc\objpas.inc:59
#1 fpc_finalize(0x11df6c4c, 0x11eb228) at ..\inc\rtti.inc:349
#2 RECORDRTTI(0x11df6c18, 0x11eb27b, {procedure (POINTER, POINTER)} 0xe97ea30) at ..\inc\rtti.inc:227
#3 CLEANUPINSTANCE(0x11df6c18) at ..\inc\objpas.inc:745
#4 FREEINSTANCE(0x11df6c18) at ..\inc\objpas.inc:436
#5 DESTROY(0x11df6c18, 0x1) at ..\objpas\classes\compon.inc:494
#6 DESTROYCOMPONENTS(0x13b814f0) at ..\objpas\classes\compon.inc:513
#7 DESTROY(0x13b814f0, 0x0) at ..\objpas\classes\compon.inc:491
#8 DESTROY(0x13b814f0, 0x0) at lclclasses.pp:135
#9 DESTROY(0x13b814f0, 0x0) at include\control.inc:5137
#10 DESTROY(0x13b814f0, 0x0) at include\wincontrol.inc:6627
#11 DESTROY(0x13b814f0, 0x0) at include\customcontrol.inc:54
#12 DESTROY(0x13b814f0, 0x0) at include\scrollingwincontrol.inc:316
#13 DESTROY(0x13b814f0, 0x1) at include\customform.inc:212
#14 FREE(0x13b814f0) at ..\inc\objpas.inc:336
#15 DESTROYJITCOMPONENT(0x119f4448, 0) at ..\designer\jitforms.pp:777
#16 DESTROYJITCOMPONENT(0x119f4448, 0x13b814f0) at ..\designer\jitforms.pp:765
#17 DELETECOMPONENT(0x119d3c38, 0x13b814f0, true) at customformeditor.pp:563
#18 PREPAREFREEDESIGNER(0x100f3cc0, true) at ..\designer\designer.pp:814
#19 CLOSEUNITCOMPONENT(0x1193bcb8, 0x21eb70, []) at sourcefilemanager.pas:7638
#20 CLOSEEDITORFILE(0x1193bcb8, 0x10155720, [CFPROJECTCLOSING]) at sourcefilemanager.pas:2768
#21 CLOSEPROJECT(0x1193bcb8) at sourcefilemanager.pas:4385
#22 DOCLOSEPROJECT(0x105300c0) at main.pp:6237
#23 MAINIDEFORMCLOSEQUERY(0x105300c0, 0x11626258, false) at main.pp:2052
#24 CLOSEQUERY(0x11626258) at include\customform.inc:2247
#25 CLOSE(0x11626258) at include\customform.inc:2157
#26 WMCLOSEQUERY(0x11626258, {MSG = 66622, WPARAM = 0, LPARAM = 0, RESULT = 0, WPARAMLO = 0, WPARAMHI = 0, WPARAMFILLER = {}, LPARAMLO = 0, LPARAMHI = 0, LPARAMFILLER = {}, RESULTLO = 0, RESULTHI = 0, RESULTFILLER = {}}) at include\customform.inc:2255
#27 DISPATCH(0x11626258, 0) at ..\inc\objpas.inc:674
#28 WNDPROC(0x11626258, {MSG = 66622, WPARAM = 0, LPARAM = 0, RESULT = 0, WPARAMLO = 0, WPARAMHI = 0, WPARAMFILLER = {}, LPARAMLO = 0, LPARAMHI = 0, LPARAMFILLER = {}, RESULTLO = 0, RESULTHI = 0, RESULTFILLER = {}}) at include\control.inc:2254
#29 WNDPROC(0x11626258, {MSG = 66622, WPARAM = 0, LPARAM = 0, RESULT = 0, WPARAMLO = 0, WPARAMHI = 0, WPARAMFILLER = {}, LPARAMLO = 0, LPARAMHI = 0, LPARAMFILLER = {}, RESULTLO = 0, RESULTHI = 0, RESULTFILLER = {}}) at include\wincontrol.inc:5407
#30 WNDPROC(0x11626258, {MSG = 66622, WPARAM = 0, LPARAM = 0, RESULT = 0, WPARAMLO = 0, WPARAMHI = 0, WPARAMFILLER = {}, LPARAMLO = 0, LPARAMHI = 0, LPARAMFILLER = {}, RESULTLO = 0, RESULTHI = 0, RESULTFILLER = {}}) at include\customform.inc:1467
#31 WNDPROC(0x11626258, {MSG = 66622, WPARAM = 0, LPARAM = 0, RESULT = 0, WPARAMLO = 0, WPARAMHI = 0, WPARAMFILLER = {}, LPARAMLO = 0, LPARAMHI = 0, LPARAMFILLER = {}, RESULTLO = 0, RESULTHI = 0, RESULTFILLER = {}}) at mainbar.pas:551
#32 DELIVERMESSAGE(0x11626258, 0) at lclmessageglue.pas:112
#33 DOWINDOWPROC(0x22a9d8) at win32\win32callback.inc:2529
#34 WINDOWPROC(918956, 16, 0, 0) at win32\win32callback.inc:2691
#35 CUSTOMFORMWNDPROC(918956, 16, 0, 0) at win32\win32wsforms.pp:386
#36 gapfnScSendMessage at :0
#37 ?? at :0
#38 USER32!GetThreadDesktop at :0
#39 WIN32WSFORMS$_$TWIN32WSSCROLLBOX_$__$$_CREATEHANDLE$TWINCONTROL$TCREATEPARAMS$$LONGWORD at :0
#40 USER32!GetThreadDesktop at :0
#41 ?? at :0
 
Irgendwie scheint die Referenzzählung nicht zu passen.

Leider bin ich zur Zeit echt eingespannt, versuche aber wenn ich demnächst Zeit habe, dies zu debuggen. Evtl. findest du ja aber schneller die Ursache?! :)

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Interface als published property geht nicht

Beitrag von Michl »

Nur kurz getestet: wenn man zur Designzeit eine Komponente (die dieses Interface hat) dem TIntfComp.ObjectHasInterface zuweist und wieder entfernt (egal, ob die Komponente auf dem Form verbelibt oder nicht), tritt dieser Fehler nicht auf.

Evtl. muss man beim Aufräumen der Komponenten, die vorziehen, die per Interface als Property verbunden sind.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Interface als published property geht nicht

Beitrag von af0815 »

Nachdem ich kein Problem mit der Zeit bei diesem privaten Projekt habe - das mit den Interface Based, geht auf eine (alte) Idee aus einem sehr interessanten Buch* zurück, Programmierung auf Interfaces nicht auf Objekte - brennt es mir nicht. Ausserdem kann ich sehr viel über den OI unter Lazarus lernen.

Eines der Probleme ist, das ich für den OI keinerlei Tests finde. Das würde die Sache wesentlich erleichtern. Tests für die Komponente IDEIntf sind bis auf einen MenueIntf nicht vorhanden. Ich gehe davon aus das der OI genaugenommen nicht getestet wird. Das erklärt für mich einiges :-)
Laut der internen Beschreibung im OI ist der sehr wohl auch standalone nutzbar, somit sollten auch Test machbar sein. Was ich jetzt einmal verstehen muss, ist wie bzw. wo die Komponenten gespeichert sind und wie man diese Speicherung nachbauen kann, damit der OI richtig läuft.

* Building Object Applications That Work von Scott W. Ambler, Cambridge University Press, ISBN 0-521-64826-2 1998 :-)
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Interface als published property geht nicht

Beitrag von Michl »

Zu den Tests und Objectinspector allgemein, kann ich leider aus dem Stegreif auch nichts sagen. Vieles versteht man, wenn man den Code liest bzw. debuggt (zumindest mir hilft es). Da Mattias als Autor am besten Bescheid wissen müsste, wäre der richtige Ort für eine Frage diesbezüglich die Lazarus Mailing-List.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Interface als published property geht nicht

Beitrag von Michl »

Wie oben vermutet, liegt der Fehler in der Reihenfolge der Freigaben. Ein einfaches Beispiel demonstiriert das (Heaptrc muss aktiviert sein):

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  FIntfComp := TIntfComp.Create(nil);
  FCompInterface := TCompInterface.Create(nil);
  FIntfComp.ObjectHasInterface := FCompInterface;
end;

gebe ich nun den Speicher wie folgt frei:

Code: Alles auswählen

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FIntfComp.Free;
  FCompInterface.Free;
end;
kommt es zu keinem SIGSEGV,

so schon:

Code: Alles auswählen

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FCompInterface);
//  FIntfComp.ObjectHasInterface := nil;  // <- auch so gibt es den SIGSEGV
  FIntfComp.Free;
end;


Bei:

Code: Alles auswählen

unit IntfComp;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs;
 
type
 
  // define the Interface
 
  { ITestInterface }
 
    ITestInterface =  interface
      ['{DC209DB2-6E2D-4680-93D6-5D9B3983C0D3}']
      function OnlyDummy: integer;
    end;
 
  { TIntfComp }
 
  TIntfComp = class(TComponent)
  private
    FObjectHasInterface: ITestInterface;
    function GetObjectHasInterface: ITestInterface;
    procedure SetObjectHasInterface(AValue: ITestInterface);
  public
    destructor Destroy; override;
  published
    property ObjectHasInterface: ITestInterface read GetObjectHasInterface write SetObjectHasInterface;
  end;
 
  { TCompInterface }
 
  TCompInterface = class(TComponent, ITestInterface)
  private
    function OnlyDummy: integer;
  end;
 
 
procedure Register;
 
implementation
 
procedure Register;
begin
  RegisterComponents('ShowBug',[TIntfComp, TCompInterface]);
end;
 
{ TCompInterface }
 
function TCompInterface.OnlyDummy: integer;
begin
  Result := -1;
end;
 
{ TIntfComp }
 
function TIntfComp.GetObjectHasInterface: ITestInterface;
begin
  Result:= FObjectHasInterface;
end;
 
procedure TIntfComp.SetObjectHasInterface(AValue: ITestInterface);
begin
  FObjectHasInterface:= AValue;
end;
 
destructor TIntfComp.Destroy;
begin
  FObjectHasInterface := nil;
  inherited Destroy;
end;
 
end.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Interface als published property geht nicht

Beitrag von af0815 »

Ich versuche etwas mehr über das Verhalten des ObjektInspektors herauszufinden, damit ich ev. mehr gegen den Bug tun kann. Dazu versuche ich jetzt den ObjektInspektor alleine im Testprogramm zu verwenden, allerdings stosse ich da auf Probleme. Ich erstelle zur Laufzeit eine Form und füge ein Panel in die Form ein, das geht soweit und sieht man auch im Test-OI. Versuche ich in das Panel noch ein Panel einzufügen, so wird das nicht im OI angezeigt. Da habe ich irgendeinen Knopf in meiner Denkweise.

Das komplette Testprojekt liegt auf Github unter https://github.com/afriess/LazarusBug0032919

Hier nur der spezifische Code

Code: Alles auswählen

procedure TForm1.BuCreateClick(Sender: TObject);
begin
  BuCreate.Enabled:= false;
  BuFree.Enabled:= not BuCreate.Enabled;
 
  DUT := TObjectInspectorDlg.Create(nil);
 
  // create the PropertyEditorHook (the interface to the properties)
  ThePropertyEditorHook:=TPropertyEditorHook.Create(DUT);
  DUT.PropertyEditorHook:=ThePropertyEditorHook;
 
  // Create components
  ATestForm :=  TForm.Create(nil);
  // Create a Panel
  ATestPanel := TPanel.Create(ATestForm);
  ATestPanel.Parent:=ATestForm;
  ATestPanel.Caption:= 'Panel';
  // Create in the panel a panel (to see the chain)
  ATestPanelSub := TPanel.Create(ATestPanel);
  ATestPanelSub.Parent := ATestPanel;
  ATestPanelSub.Caption:= 'Sub Panel';
 
  ThePropertyEditorHook.LookupRoot:=ATestForm;
 
  PerList:=TPersistentSelectionList.Create;
  PerList.Add(ATestForm);
  DUT.Selection:=PerList;
  PerList.Free;
 
  DUT.Show;
end;
 


Wo habe ich da den Fehler gemacht ?

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Interface als published property geht nicht

Beitrag von Michl »

af0815 hat geschrieben:damit ich ev. mehr gegen den Bug tun kann
Ich hatte diesen mal zwei Tage lang sehr intensiv debuggt. Siehe auch http://www.lazarusforum.de/viewtopic.php?f=19&t=11350. Es liegt tatsächlich an der Reihenfolge, wie die Objekte freigegeben werden. Wenn das per Interface verbundene Objekt länger lebt, als das, was es eingebunden hat, ist alles gut. Sobald das per Interface eingebundene Objekt vorher gelöscht wird, kommt es zum SIGSEGV. Dazu muss man auch nicht Lazarus beenden, dies funktioniert auch, wenn man so ein Objekt erstellt, verbindet, löscht und dieses wiederholt (erst beim zweiten mal gibt es den SIGSEGV).

Bis dato hatte ich keine Möglichkeit gefunden, dem verbundenen Objekt mitzuteilen, daß bevor das per Interface verbundene Objekt gelöscht wird, dieses beim "Parent" auf nil zu setzen. Wenn ich mich recht erinnere, waren mehr oder weniger die Versuche letztlich an der Reffernzzählung gescheitert. Komischerweise funktioniert diese, wenn man händisch zuerst das Interface Objekt vom Parent entfernt. Dann geht das Löschen beider Objekte, egal welche Reihenfolge.

Zum aktuellen Problem (Standalone OI) kann ich nicht viel sagen. Mir fehlt es zur Zeit auch an Zeit, mich da hinein zu denken (in ein paar Wochen habe ich hoffentlich den Kopf wieder etwas freier). Vermutlich wirst du bessere/schnellere Antworten in der Lazarus-Mailing-List bekommen (afaik liest hier Mattias nicht mit).

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Interface als published property geht nicht

Beitrag von af0815 »

Michl hat geschrieben:Zum aktuellen Problem (Standalone OI) kann ich nicht viel sagen.

Der Standalone OI wurde von mir genommen um zu sehen ob das Problem auch ohne Debugging von Lazarus selbst zu isolieren ist. Die Probleme sind im Objectinspector aufgetreten bzw. im Bereich der Propertyeditoren. Deswegen habe ich versucht den OI als getrennte Einheit anzusehen, da auch die Kommentare im OI selbst darauf schliessen gelassen haben, das man den Universeller verwenden kann. Deswegen habe ich angefangen mich mit dem auseinanderzusetzen (im wahrsten Sinne des Wortes :-) ).

Danke nochmals

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Soner
Beiträge: 622
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Interface als published property geht nicht

Beitrag von Soner »

af0815 hat geschrieben:Ich versuche etwas mehr über das Verhalten des ObjektInspektors herauszufinden, damit ich ev. mehr gegen den Bug tun kann. Dazu versuche ich jetzt den ObjektInspektor alleine im Testprogramm zu verwenden, allerdings stosse ich da auf Probleme. Ich erstelle zur Laufzeit eine Form und füge ein Panel in die Form ein, das geht soweit und sieht man auch im Test-OI. Versuche ich in das Panel noch ein Panel einzufügen, so wird das nicht im OI angezeigt. Da habe ich irgendeinen Knopf in meiner Denkweise.

Das komplette Testprojekt liegt auf Github unter https://github.com/afriess/LazarusBug0032919

Hier nur der spezifische Code

Code: Alles auswählen

procedure TForm1.BuCreateClick(Sender: TObject);
begin
  BuCreate.Enabled:= false;
  BuFree.Enabled:= not BuCreate.Enabled;
 
  DUT := TObjectInspectorDlg.Create(nil);
 
  // create the PropertyEditorHook (the interface to the properties)
  ThePropertyEditorHook:=TPropertyEditorHook.Create(DUT);
  DUT.PropertyEditorHook:=ThePropertyEditorHook;
 
  // Create components
  ATestForm :=  TForm.Create(nil);
  // Create a Panel
  ATestPanel := TPanel.Create(ATestForm);
  ATestPanel.Parent:=ATestForm;
  ATestPanel.Caption:= 'Panel';
  // Create in the panel a panel (to see the chain)
  ATestPanelSub := TPanel.Create(ATestPanel);
  ATestPanelSub.Parent := ATestPanel;
  ATestPanelSub.Caption:= 'Sub Panel';
 
  ThePropertyEditorHook.LookupRoot:=ATestForm;
 
  PerList:=TPersistentSelectionList.Create;
  PerList.Add(ATestForm);
  DUT.Selection:=PerList;
  PerList.Free;
 
  DUT.Show;
end;
 


Wo habe ich da den Fehler gemacht ?

Andreas


Wenn man von ATestPanelSub als Eigentümer den Form angibt wird es in Komponententree sichtbar:
ATestPanelSub := TPanel.Create(ATestForm);

ZIemlich merkwürdiges verhalten, jedenfalls gibt es im ORdner Lazarus/Examples/objectinspector funktionierendes Beispiel.

Soner
Beiträge: 622
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Interface als published property geht nicht

Beitrag von Soner »

Ich habe jezt im Beispiel von Lazarus\examples\objectinspector ein Subpanel wie in dein Beispiel erstellt, da wird es auch nicht angezeigt wenn man Parent als Eigentümer angibt, mann muß Root-Komponente(hier Form1) angeben damit es angezeigt wird. Entweder Bug in Komponententree oder gewollt.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Interface als published property geht nicht

Beitrag von af0815 »

Es geht jetzt, dank eines Hints von Mathias Gärtner, Beim Create MUSS man immer die Form/Frame angeben, als Parent dann denjenigen dessen Parent das ist. Es geht jetzt auch mit meinen Code bei Github.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6197
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

[gelöst] Interface als published property geht nicht

Beitrag von af0815 »

Zück zu meinem eigentlich Problem, sieh Überschrift. Da habe ich gerade einen Tip von Martin Schreiber auf der Mailingliste bekommen:

Code: Alles auswählen

If you want to set a COM interface pointer to nil without calling _release() 
use
" pointer(FObjectHasInterface):= nil;"
 


Ich habe in mein Object einen eigenen destructor eingefügt, der die Verbindung zum Interface bei abräumen der Objekte korrekt auflöst. Wenn man das Objekt einfach nil setzt, so wird im Hintergrund automatisch ein _Release ausgelöst und damit das Interface-Objekt hier falsch gelöscht.

Code: Alles auswählen

destructor TIntfComp.Destroy;
begin
  pointer(FObjectHasInterface):= nil;
//  FObjectHasInterface:= nil;
  inherited Destroy;
end;
 

Martin hat das auf der Mailingliste sehr schön mit der Begriffserklärung klar gemacht.

Code: Alles auswählen

 It is 
an undefined "implementation detail" when the interface variable goes out of
scope, there is a risk that it goes out of scope and _release() will be
called after the according object has been destroyed by a TObject.Destroy()
call.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: [gelöst] Interface als published property geht nicht

Beitrag von mse »

Ein wichtiger Hinweis in diesem Zusammenhang:
Wenn man keine Interface-Referenzzählung benötigt, sondern die Lebensdauer der Objekte mittels TObject.Destroy()/Free() oder dem TComponent.Owner-Mechanismus steuern will, sollte man CORBA-Interfaces verwenden.

Code: Alles auswählen

 
{$interfaces corba}
type
 meincorbainterface = interface
[...]
 end;
 

COM-Interface sollten meiner Meinung nach lediglich zur Kommunikation mit externen Windows Active-X-Elementen verwendet werden, innerhalb einer FPC-Anwendung sollten sie nicht eingesetzt werden. Sehr problematisch ist die Kombination von TComponent und COM-Interface.
Als MSEgui noch Delphi-kompatibel war, war ich bei der Delphi-Version gezwungen COM Interface einzusetzen, da Delphi keine nicht-referenz-gezählten Interface kennt. Es war ein Albtraum...

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: [gelöst] Interface als published property geht nicht

Beitrag von Michl »

@mse: Eigentlich funktionieren jetzt COM Interface Properties im Designer von Lazarus. Soviel ich sehen kann, haben wir zur Zeit nur das Problem, wenn das verbundene Objekt zuerst gelöscht wird, daß dieses auch auf nil im Property gesetzt wird. Das funktioniert unter 32bit so:

Code: Alles auswählen

{$interfaces com}
  ITestInterface = interface
    ['{DC209DB2-6E2D-4680-93D6-5D9B3983C0D3}']
    function OnlyDummy: integer;
  end;
{$interfaces com}     
 
  TIntfComp = class(TComponent)
  private
    FObjectHasInterface: ITestInterface;
    function GetObjectHasInterface: ITestInterface;
    procedure SetObjectHasInterface(AValue: ITestInterface);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    destructor Destroy; override;
  published
    property ObjectHasInterface: ITestInterface read GetObjectHasInterface write SetObjectHasInterface;
  end;
 
...
 
procedure TIntfComp.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  // if ObjectHasInterface is being removed then we need to make sure it is
  // removed from here too
  // WriteLn('TIntfComp.Notification ', dbgs(PtrInt(AComponent)), ' ', dbgs(PtrInt(ObjectHasInterface)), ' ', dbgs(SizeOf(AComponent)));
  if (Operation = opRemove) and (PtrInt(AComponent) + $30 = PtrInt(ObjectHasInterface)) then
    Pointer(FObjectHasInterface) := nil;
end;

Kannst du mir erklären, warum ich unter 32bit ein Offset von 48 und bei 64bit ein Offset von 88 habe, wenn ich testen will, ob der Zeiger auf AComponent gleich Zeiger auf ObjectHasInterface ist?! (ObjectHasInterface ist eine Instanz von TCompInterface = class(TComponent, ITestInterface) )

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Antworten