2

I want to implement an interface, but I cannot declare it from TInterfacedObject, and in this case the documentation says that I must implement QueryInterface, _AddRef, _Release. But I am not sure if this is mandatory. I haven't really worked with interfaces so far... They say something about COM objects, but I don't even know what that means. I never intentionally used COM objects, maybe only if they are included in some pieces of code which I took from the internet. I tried an example (see below) and it's working without implementing those 3 methods. So...

  1. How should I know if I must implemet them ?
  2. If I must implement them, how can I do it ? I don't know nothing about those methods should do. Should I copy the implementation from TInterfacedObject ? (In fact, it has more than 3... those extra ones must be copied too ?)

Thanks !

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type

  IShellInterface = interface
  ['{55967E6B-5309-4AD0-B6E6-739D97A50626}']
   procedure SetPath(const APath: String);
   function GetPath: String;
  end;

  TDriveBar = class(TCustomPanel, IShellInterface)
  private
   FDriveLink: IShellInterface;
   FPath: String;
  public
   procedure SetPath(const APath: String);
   function  GetPath: String;
   property  DriveLink: IShellInterface read FDriveLink write FDriveLink;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    DriveBar1, DriveBar2: TDriveBar;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TDriveBar.SetPath(const APath: String);
begin
 FPath:= APath;
 Caption:= APath;
end;

function TDriveBar.GetPath: String;
begin
 Result:= FPath;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 ReportMemoryLeaksOnShutdown:= True;

 DriveBar1:= TDriveBar.Create(Form1);
 DriveBar1.Parent:= Form1;
 DriveBar1.SetBounds(20, 20, 250, 40);
 DriveBar1.SetPath('Drive Bar 1');

 DriveBar2:= TDriveBar.Create(Form1);
 DriveBar2.Parent:= Form1;
 DriveBar2.SetBounds(20, 80, 250, 40);
 DriveBar2.SetPath('Drive Bar 2');

 DriveBar1.DriveLink:= DriveBar2;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 DriveBar1.DriveLink.SetPath('D:\Software\Test');
 Caption:= DriveBar2.GetPath;
end;

end.
Marus Gradinaru
  • 2,824
  • 1
  • 26
  • 55
  • It is not clear what you are asking. You example inherits from TIntefacedObject, so those methods are already implemented. – Dalija Prasnikar Jan 29 '21 at 13:21
  • Short answer, yes you always need to implement those methods in classes that support interfaces. How you should implement them depends on various things and first thing you need to answer whether you want class to have automatic reference counting enabled or not. – Dalija Prasnikar Jan 29 '21 at 13:23
  • Ah, sorry ! I corrected. There should be `TCustomPanel`... – Marus Gradinaru Jan 29 '21 at 13:25
  • 3
    TCustomPanel is TComponent descendant and TComponent has those methods implemented, you don't have to do a thing. Also, if you inherit from class that does not have those methods you would not be able to compile your code. – Dalija Prasnikar Jan 29 '21 at 13:26
  • Wow, I'm glad to hear that ! What a luck ! :) – Marus Gradinaru Jan 29 '21 at 13:34

1 Answers1

3

Any class that supports interfaces must implement all three methods:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;

If base class you inherit from has those methods implemented, then you don't need to implement them. If the base class does not have them, your code will not compile.

Different classes have different implementations of those methods and primary and the most important difference is whether automatic reference counting is enabled or not.

If the reference counting is enabled, then you should always store instances of such class in interface reference (variable type must be interface), if it is disabled, they you can use object reference.

If enabled ARC will automatically manage memory of such instance, and if disabled you need to release such instance manually.

For example, class where reference counting is enabled is TInterfacedObject and class where it is disabled for most uses is TComponent (except in cases where it serves as COM object container).

Basically, in most simple scenario, if the _AddRef and _Release just return -1 then reference counting will be disabled. If you need to implement reference counting, then the best template to copy code or inherit from is TInterfacedObject.

Purpose of QueryInterface is adding functionality for Support function and querying interface for supported GUIDs. If you are not sure what to put there, you can always use TInterfacedObject implementation regardless of whether you have ARC enabled or not.

It is also possible to have class that can have ARC disabled or enabled depending on same value passed to the constructor. Example of such class is TXMLDocument where if nil is passed as Owner enables ARC and otherwise ARC is disabled.


In your example, TCustomPanel inherits from TComponent that already has those methods implemented, so you don't have to do a thing.

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159