Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Supports ohne out Parameter (https://www.delphipraxis.net/170914-supports-ohne-out-parameter.html)

pflaume 10. Okt 2012 09:28

Delphi-Version: 2009

Supports ohne out Parameter
 
Hallo,

Ich werde nicht so recht schlau aus dem was genau bei den folgenden Situationen passiert.
Es geht um diese Funktion aus der SysUtils.pas:

Delphi-Quellcode:
function Supports(const Instance: TObject; const IID: TGUID): Boolean;
Wenn man das unten stehende Programm ausführt, verabschiedet es sich an markierter Stelle #1 mit einer
EInvalidPointer Exception (wahrscheinlich beim Freigeben der temporären Variable aus einer anderen
Supports() Überladung).

Delphi-Quellcode:
program Project38;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  IFirst = interface
    ['{BC927DB0-9B4D-49C4-8460-6714605F4F20}']
  end;

  ISecond = interface
    ['{F7DBA334-8216-4435-AD56-2E9465CE975B}']
  end;

  TFirstSecond = class(TInterfacedObject, IFirst, ISecond)
  end;

var
  a: TFirstSecond;
  f: IFirst;
  s: ISecond;

begin
  a := TFirstSecond.Create;

  if Supports(a, IFirst) then
    Writeln('a supports IFirst');

  if Supports(a, ISecond) then // #1
    Writeln('a supports ISecond');

  if Supports(a, IFirst, f) then
    Writeln('[out] a supports IFirst');

  if Supports(a, ISecond, s) then
    Writeln('[out] a supports ISecond');

  Readln;
end.
Ändert man das folgende Programm ab und ersetzt die Writeln Aufrufe durch ShowMessage,
verschwindet dieser Fehler.

Delphi-Quellcode:
program Project38;

{$APPTYPE CONSOLE}

uses
  Dialogs,
  SysUtils;

type
  IFirst = interface
    ['{BC927DB0-9B4D-49C4-8460-6714605F4F20}']
  end;

  ISecond = interface
    ['{F7DBA334-8216-4435-AD56-2E9465CE975B}']
  end;

  TFirstSecond = class(TInterfacedObject, IFirst, ISecond)
  end;

var
  a: TFirstSecond;
  f: IFirst;
  s: ISecond;

begin
  a := TFirstSecond.Create;

  if Supports(a, IFirst) then
    ShowMessage('a supports IFirst');

  if Supports(a, ISecond) then
    ShowMessage('a supports ISecond');

  if Supports(a, IFirst, f) then
    ShowMessage('[out] a supports IFirst');

  if Supports(a, ISecond, s) then
    ShowMessage('[out] a supports ISecond');

  Readln;
end.
Hinzu kommt das der erste Aufruf von Supports() ohne out Parameter, scheinbar
die Variable a verändert. Leider reicht mein Assemblerwissen nicht ganz aus
um alles nachzuvollziehen was in class function TObject.GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
geschiet.
Nach meinem Verständnis müssten alle vier Meldungen angezeigt werden, stattdessen wird nur die erste angezeigt.

Vielleicht kann jemand Licht ins Dunkel bringen :)

himitsu 10. Okt 2012 09:41

AW: Supports ohne out Parameter
 
Verwende mal TComponent, statt dem TInterfacedObject. [edit] oder TInterfacedPersistent, dessen Name ich ständig vergesse (Dank an Uwe)

Du nutzt das TFirstSecond als Objekt-Instanz, womit es intern keine Interfacereferenz gibt.
Suppots greift aber intern über ein Interface zu, womit also eine Interface-Referenz erstellt wird. Bei Freigabe dieser Referenz wird dann das ganze Objekt freigegeben, da es keine weiteren Referenzen gibt.

In TComponent wird die Referenzzählung der Interfaces "ignoriert" und diese steuern vorallem nicht die Freigabe.

xaromz 10. Okt 2012 09:42

AW: Supports ohne out Parameter
 
Hallo,

ich vermute einfach mal (hab gerade kein Delphi zur Hand), dass Supoorts intern AddRef / Release aufruft. Durch das Release wird dann das Objekt freigegeben und schon knallt's.
Eine mögliche Lösung (die viele Probleme vermeidet) ist, immer mit Interfaces zu arbeiten und nie mit dem konkreten Objekt. Alternativ gibt es noch ein Basisobjekt, das die Referenzzählung ausschaltet. Da musst Du aber wissen, was Du tust. Solltest Du aber bei Interfaces immer :)

Gruß
xaromz

Uwe Raabe 10. Okt 2012 09:47

AW: Supports ohne out Parameter
 
Die anderen haben es ja schon erklärt. Das Support ohne out-Parameter ruft intern das mit out-Parameter auf, wobei das temporäre Interface beim Verlassen der Funktion freigegeben wird. Da a ein TFirstSecond ist, was von TInterfacedObject abgeleitet wurde, ist der interne Referenzzähler nach dem Create Null. Wenn jetzt die implizite Referenzzählung ein Add/Release ausführt, wird beim Release der Referenzzähler wieder zu Null und die Instanz wird freigegeben. Ab dann zeigt a auf eine nicht mehr existente Instanz.

Wenn du sowohl mit Interfaces als auch mit Objekt-Instanzen arbeiten willst, kannst du statt TInterfaceObject auch TInterfacedPersistent nehmen.

pflaume 10. Okt 2012 11:10

AW: Supports ohne out Parameter
 
Danke für die Antworten, das mit Referenzzählung hätte mir auffallen müssen, aber
was ist mit dem zweiten Teil der Frage?
Wie und warum genau verändert Supports() die Instanz so das bei dem Aufruf von
Supports(a, ISecond) als Rückgabe Wert false rauskommt?

himitsu 10. Okt 2012 11:20

AW: Supports ohne out Parameter
 
Zitat:

Zitat von pflaume (Beitrag 1186479)
aber was ist mit dem zweiten Teil der Frage?

Wenn du TInterfacedPersistent verwendest, dann passiert das bei dir immernoch?

Thom 10. Okt 2012 11:27

AW: Supports ohne out Parameter
 
Ich verstehe nicht, weshalb Dir alle raten, nicht TInterfacedObject zu verwenden... Ändere einfach
Delphi-Quellcode:
var
  a: TFirstSecond;
in
Delphi-Quellcode:
var
  a: IInterface;
und alles funktioniert.

Uwe Raabe 10. Okt 2012 11:36

AW: Supports ohne out Parameter
 
Zitat:

Zitat von pflaume (Beitrag 1186479)
Danke für die Antworten, das mit Referenzzählung hätte mir auffallen müssen, aber
was ist mit dem zweiten Teil der Frage?
Wie und warum genau verändert Supports() die Instanz so das bei dem Aufruf von
Supports(a, ISecond) als Rückgabe Wert false rauskommt?

Offensichtlich war meine Erklärung nicht verständlich genug: Es ist nicht das QueryInterface was die Referenzzählung auslöst, sondern die Implementierung des Supports ohne out-Parameter:

Delphi-Quellcode:
function Supports(const Instance: IInterface; const IID: TGUID): Boolean;
var
  Temp: IInterface;
begin
  Result := Supports(Instance, IID, Temp);
end;
Nach dem
Delphi-Quellcode:
a := TFirstSecond.Create
ist der Referenzzähler von a = 0. Beim ersten Aufruf von Supports wird in der Support-Funktion für Temp ein implizites AddRef und Release ausgelöst, da beim Verlassen der Funktion alle lokalen Interface-Variablen auf nil gesetzt werden. Beim Release springt der Referenzzähler wieder auf 0 und die Instanz wird freigegeben. Danach verweist a nicht mehr auf eine gültige Instanz und es geschehen nicht vorhersagbare Dinge.

Uwe Raabe 10. Okt 2012 11:40

AW: Supports ohne out Parameter
 
Zitat:

Zitat von Thom (Beitrag 1186487)
Ich verstehe nicht, weshalb Dir alle raten, nicht TInterfacedObject zu verwenden... Ändere einfach
Delphi-Quellcode:
var
  a: TFirstSecond;
in
Delphi-Quellcode:
var
  a: IInterface;
und alles funktioniert.

Dieses Vorgehen ist aber nicht in jedem Fall anwendbar. Man muss sich schon entscheiden, ob man nur mit Interface-Referenzen und Referenzzählung arbeiten will, oder ob man ohne Referenzzählung selber die Objekt-Instanzen verwalten will. Für beide Vorgehen gibt es je nach Anwendungsfall gute Gründe. Man muss dann nur die passende Elternklasse verwenden.

Uwe Raabe 10. Okt 2012 11:51

AW: Supports ohne out Parameter
 
War im Beispiel der falsche Overload der Funktion - der richtige enthält übrigens eine entsprechende Warnung:

Delphi-Quellcode:
function Supports(const Instance: TObject; const IID: TGUID): Boolean;
var
  Temp: IInterface;
begin
  // NOTE: Calling this overload on a ref-counted object that has REFCOUNT=0
  // will result in it being freed upon exit from this routine.
  Result := Supports(Instance, IID, Temp);
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:31 Uhr.
Seite 1 von 2  1 2      

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