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/)
-   -   Supports(..) liefert Referenz welche AV auslöst (https://www.delphipraxis.net/183554-supports-liefert-referenz-welche-av-ausloest.html)

Der schöne Günther 19. Jan 2015 18:50

Delphi-Version: XE7

Supports(..) liefert Referenz welche AV auslöst
 
Ich möchte kein konkretes Problem lösen. Ich möchte nur verstehen warum folgender Code mit der Zeile
Delphi-Quellcode:
if castSuccessful then myIntf.doSomething();
eine
Zitat:

Im Projekt Project3.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x00000001: read of address 0x00000001' aufgetreten.
wirft:

Delphi-Quellcode:
program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
   IMyInterface = interface
   ['{88DDD9A5-F4BC-47B9-9240-31B1C986F230}']
      procedure doSomething();
   end;

   TMyClass = class(TInterfacedObject, IMyInterface)
      public
            constructor Create();
         procedure doSomething();
   end;

procedure justSupportsThings();
var
   myObject:   TObject;
   myIntf:      IMyInterface;
   castSuccessful:   Boolean;
begin
   myObject := TMyClass.Create();
   castSuccessful := Supports(myObject, IInterface, myIntf);
   if castSuccessful then myIntf.doSomething();
end;

constructor TMyClass.Create();
begin
   _AddRef(); // Damit mich das Supports(..) nicht abräumt
end;

procedure TMyClass.doSomething;
begin
   // nop
end;

begin
  try
   justSupportsThings();
  except
   on E: Exception do
     Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.


Mir ist klar, dass im Aurfuf
Delphi-Quellcode:
Supports(myObject, IInterface, myIntf);
die letzten beiden Parameter nicht wirklich zusammenpassen, denn
Delphi-Quellcode:
myIntf
ist vom Typ
Delphi-Quellcode:
IMyInterface = Interface(IInterface)
.
Bedeutet das etwa dass
Delphi-Quellcode:
Supports(..)
die Referenz nur soweit "befüllt" wie ich mit der GUID angebe?
Ich habe mich mit den ganzen Dingen im Hintergrund (VMT, all das) nie beschäftigt...

Sir Rufo 19. Jan 2015 19:37

AW: Supports(..) liefert Referenz welche AV auslöst
 
Nur so fürs Protokoll, versuche es mal so
Delphi-Quellcode:
program dp_183554;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  IMyInterface = interface
    ['{88DDD9A5-F4BC-47B9-9240-31B1C986F230}']
    procedure doSomething( );
  end;

  TMyClass = class( TInterfacedObject, IMyInterface )
  public
    constructor Create( );
    procedure doSomething( );
  end;

procedure justSupportsThings( );
var
  myObject: TObject;
  myIntf: IMyInterface;
  castSuccessful: Boolean;
begin
  myObject := TMyClass.Create( );
  castSuccessful := Supports( myObject, IMyInterface, myIntf );
  if castSuccessful
  then
    myIntf.doSomething( );
end;

constructor TMyClass.Create( );
begin
  inherited;
  // Schwachfug
  //  _AddRef(); // Damit mich das Supports(..) nicht abräumt
end;

procedure TMyClass.doSomething;
begin
  // nop
end;

begin
  try
    justSupportsThings( );
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  readln;

end.

Der schöne Günther 19. Jan 2015 20:17

AW: Supports(..) liefert Referenz welche AV auslöst
 
Natürlich, der Leitsatz aus deiner Signatur greift einfach immer. 8-)

Aber das war mir klar, meine Frage ist ja auch eine andere

Uwe Raabe 19. Jan 2015 21:25

AW: Supports(..) liefert Referenz welche AV auslöst
 
Supports liefert dir im dritten Parameter einen Zeiger auf die VMT genau des Interfaces, das du abfragst. Da in diesem Fall die Typsicherheit nicht greift, da der Parameter typlos ist, musst du selbst dafür sorgen, daß die Interface-Variable dort auch den passenden Typ hat (nämlich den, den du abfragst).

Sir Rufo 19. Jan 2015 22:40

AW: Supports(..) liefert Referenz welche AV auslöst
 
Ihr müsst ja auch nicht gleich alle auf mir rumhacken :mrgreen:

Dann versuch mal das hier
Delphi-Quellcode:
type
  IMyInterface = interface
    ['{88DDD9A5-F4BC-47B9-9240-31B1C986F230}']
    procedure doSomething( );
  end;

  TMyClass = class( TInterfacedObject, IInterface, IMyInterface ) // <- Augen auf
  public
    constructor Create( );
    procedure doSomething( );
  end;

procedure justSupportsThings( );
var
  myObject: TObject;
  myIntf: IMyInterface;
  castSuccessful: Boolean;
begin
  myObject := TMyClass.Create( );
  castSuccessful := Supports( myObject, IInterface, myIntf );
  if castSuccessful
  then
    myIntf.doSomething( ); // <- beim Eierkauf - Keine Exception mehr :o)
end;

Stevie 19. Jan 2015 22:59

AW: Supports(..) liefert Referenz welche AV auslöst
 
Hach ja, das alte Problem, dass "vererbte" Interfaces nicht implizit in der Klasse landen...

Uwe Raabe 20. Jan 2015 08:56

AW: Supports(..) liefert Referenz welche AV auslöst
 
Hier mal das Originalbeispiel etwas erweitert. Man vergleiche die Reihenfolge der Methodenaufrufe im Source mit der der tatsächlich ausgeführten Methoden.

Delphi-Quellcode:
program Project4;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Classes;

type
  IMyInterface = interface
    ['{88DDD9A5-F4BC-47B9-9240-31B1C986F230}']
    procedure doSomething();
  end;

  IMyInterface2 = interface
    ['{D9B8FC6D-2F71-4E31-A5E7-0CCA98146E78}']
    procedure doSomeotherthing();
  end;

  TMyClass = class(TInterfacedPersistent, IMyInterface, IMyInterface2)
  public
    procedure doSomething();
    procedure doSomeotherthing();
  end;

procedure justSupportsThings();
var
  myObject: TObject;
  myIntf: IMyInterface;
  myIntf2: IMyInterface2;
begin
  myObject := TMyClass.Create();
  try
    if Supports(myObject, IMyInterface, myIntf) then
      myIntf.doSomething();

    if Supports(myObject, IMyInterface2, myIntf2) then
      myIntf2.doSomeotherthing();

    if Supports(myObject, IMyInterface2, myIntf) then
      myIntf.doSomething();

    if Supports(myObject, IMyInterface, myIntf2) then
      myIntf2.doSomeotherthing();

  finally
    myObject.Free;
  end;
end;

procedure TMyClass.doSomeotherthing;
begin
  Writeln('Someotherthing');
end;

procedure TMyClass.doSomething;
begin
  Writeln('Something');
end;

begin
  try
    justSupportsThings();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;

end.

himitsu 20. Jan 2015 11:20

AW: Supports(..) liefert Referenz welche AV auslöst
 
Zitat:

Delphi-Quellcode:
_AddRef(); // Damit mich das Supports(..) nicht abräumt

Da ist aber jemand selber Schuld!

Entweder man benutzt nur Interface-Referenzen oder nur Objekt-Referenzen.
Nur z.B. bei TComponent-Nachfahren kann man Beides benutzen, da dort die Referenzzählung deaktiviert wurde.

Sir Rufo 20. Jan 2015 11:49

AW: Supports(..) liefert Referenz welche AV auslöst
 
@himitsu

Delphi-Referenz durchsuchenTInterfacedPersistent ;)

Und man kann sich auch eine eigene Interfaced-Klasse bauen, die keine Referenzzählung hat.

mjustin 20. Jan 2015 12:06

AW: Supports(..) liefert Referenz welche AV auslöst
 
Zitat:

Zitat von himitsu (Beitrag 1287123)
Nur z.B. bei TComponent-Nachfahren kann man Beides benutzen, da dort die Referenzzählung deaktiviert wurde.

Benutzen schon, aber eine Komponente einfach wie im folgenden Beispiel um ein Interface zu erweitern geht nur um den Preis von Memory Leaks. Der folgende Code erzeugt ein Leak einer Instanz von TMyImplementation:
Delphi-Quellcode:
program LeakTest;

uses
  Classes;

type
  MyInterface = interface
  end;

  TMyImplementation = class(TComponent, MyInterface)
  end;

  TMyContainer = class(TObject)
  private
    FInt: MyInterface;
  public
    property Impl: MyInterface read FInt write FInt;
  end;

var
  C: TMyContainer;
begin
  ReportMemoryLeaksOnShutdown := True;

  C := TMyContainer.Create;
  C.Impl := TMyImplementation.Create(nil);
  C.Free;
end.
Das Memory Leak ergibt sich nur bei TComponent als Basisklasse. Mit TInterfacedObject als Basisklasse wird das über Interface verwaltete Feld FInt beim Destroy mit freigegeben, und kein Leak bleibt zurück.

Unter Stackoverflow kann man den Hintergrund dieses Verhaltens detailliert nachlesen.
Why do interface implementations based on TComponent leak memory?


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:20 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