user1627960
user1627960

Reputation: 431

How to implement an interface in a delphi control

I have read that if a class implements an interface it is now reference counted and should not manage its memory by calling free .

However, if you make a custom control and have it implement an interface, how do you prevent its owner from managing its memory? For example when you drop it on a form at design time will the reference counting and owner memory management fight?

Thanks for your time.

Upvotes: 4

Views: 1701

Answers (1)

Johan
Johan

Reputation: 76537

Controls are except from this behavior, because they do not inherit from TInterfacedObject.
As such they do not do reference counting, their ref count is stuck at -1 by design*).

All controls inherit from TComponent which looks like this:

TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)  

The reference counting in TComponent looks like this:

function TComponent._AddRef: Integer;
begin
  if FVCLComObject = nil then Result := -1
       // -1 indicates no reference counting is taking place
  else Result := IVCLComObject(FVCLComObject)._AddRef;
end;

function TComponent._Release: Integer;
begin
  if FVCLComObject = nil then Result := -1   
  // -1 indicates no reference counting is taking place
  else Result := IVCLComObject(FVCLComObject)._Release;
end;

*)The caveat being that if VCLComObject is assigned, they follow that Object's reference counting (which is normally not stuck at -1).
VCLComObject is nil for most components.
It is only used for component wrappers generated by the IDE to wrap COM objects.
See: TComponent.ComObject

So you can add interfaces to your heart's content. It will work with no problems, as long as you remember to free the Component when you're done.

You can test to see if a control does reference count or not by testing:

DoesNotRefCount:= Supports(MyObject, IInterfaceComponentReference) 
                  and (TComponent(MyObject).VCLComObject = nil);  

Do not call _AddRef on your object to test if it returns -1, because that may break objects that do use ref counting.
If your ref counted objects starts at 0 and you do an _AddRef followed by a _Release you will destroy the object, even though Delphi was just about to call _AddRef two instructions further down the line.

If you want to make your own objects that don't do reference counting, it may be a good idea to add a marker interface as well:

INoRefCounting = interface
 ['{CAD60ADF-C49A-46FB-BB5A-CC54BD22C7EB}']
end;

In newer Delphi's you can descent from TSingletonImplementation which does the dummy no-ref counting for you.
In older Delphi's descent from TObject (or TWhatever) and implement the no-ref counting like so:

function TMyObject.QueryInterface(const IID: TGUID; out Obj): HResult; {stdcall;}
begin
  if GetInterface(IID, Obj) then Result := S_OK
  else Result := E_NOINTERFACE;
end;

function TMyObject._AddRef: Integer; {stdcall;}
begin
  Result := -1;
end;

function TMyObject._Release: Integer; {stdcall;}
begin
  Result := -1;
end;  

Final note
If you want to make 100% sure your custom control does not do reference counting, you'll have to override the _AddRef/_Release methods to remove the conditional reference counting based on VCLComObject.

Warning
If the lifetime of your controls is short and you're still holding references to interfaces of those controls for longer then you're going to run into problems.
If you're suffering from this it might be worthwhile to add debugging code to the _AddRef, _Release and Destroy methods where you keep track of reference counts and signal if the refcounts reaches zero to late or too soon.

Upvotes: 6

Related Questions