Delphi interfaces without reference counting

Any interface in Delphi inherits from IInterface (which is a nickname for IUnknown). It is nice for reference-counted interfaces, but sometimes we do not need reference counting at all. Consider the following example:

unit IValues;

interface

type
  IValue = interface(IInterface)
    function GetValue: Integer;
  end;

function GetIValue: IValue;

implementation

type
  TValueObject = class(TObject, IValue)
  protected
    FValue: Integer;
    function GetValue: Integer;
  public
    constructor Create(AValue: Integer);
  end;

var
  FValueObject: TValueObject;

function GetIValue: IValue;
begin
  Result:= FValueObject;
end;

constructor TValueObject.Create(AValue: Integer);
begin
  FValue:= AValue;
end;

function TValueObject.GetValue: Integer;
begin
  Result:= FValue;
end;

initialization
  FValueObject:= TValueObject.Create(11);

finalization
  FValueObject.Free;

end.

The above code does not compile because TValueObject does not implement IUnknown. A workaround is to implement stub methods for IUnknown:

type
  TValueObject = class(TObject, IValue)
  protected
    FValue: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function GetValue: Integer;
  public
    constructor Create(AValue: Integer);
  end;
 
function TValueObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result:= S_OK
  else
    Result:= E_NOINTERFACE;
end;

function TValueObject._AddRef: Integer;
begin
  Result:= -1;
end;

function TValueObject._Release: Integer;
begin
  Result:= -1;
end;

You can find the same code in VCL sources (TCustomVariantType class from Variants.pas unit).

Now, why should I implement IUnknown in my code if I don’t need it at all?

The above implementation of IUnknown looks like a standard implementation for non-reference-counted interfaces in Delphi. Why not to include it into System.pas and apply compiler magic to make the code in the first example compilable and legitimate? The implementation does not use additional object fields (TInterfacedObject implementation requires additional FRefCount field), so it seems like all that is required from the compiler is to insert pointers to the default IUnknown method’s implementations into the interface tables of any class that does not implement or inherit these methods can be implemented directly in TObject. It will save the programmer from writing a useless duplicate code. The compiler may also issue a warning for the safety reasons, if necessary.

Advertisements

13 thoughts on “Delphi interfaces without reference counting

  1. Any implemented interface requires a hidden 4 byte field in the data of the object. An interface pointer is a pointer to a pointer to an array of pointers for the methods. That “pointer to an array of pointers for the methods” is what is being stored in the hidden field.

    So adding IUnknown to TObject would increase the size of all objects on the heap by 4 bytes.

    • That is true if only IUnknown is actually implemented in TObject, but I did not mean it. I meant any class that implements some interface but does not implement IUnknown methods may inherit the IUnknown part of the interface from TObject

  2. TValueObject can subclass TSingletonImplementation from Generics.Defaults instead of TObject to get the implementation for the missing methods.

    /Micke

  3. You can also use the TSingletonImplementation from the Generics.Defaults unit as a base class:

    //MMWIN:CLASSCOPY
    unit _MM_Copy_Buffer_;
    
    interface
    
    type
      // A non-reference-counted IInterface implementation.
      TSingletonImplementation = class(TObject, IInterface)
      protected
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
      end;
    
    
    implementation
    
    
    { TSingletonImplementation }
    
    function TSingletonImplementation.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      if GetInterface(IID, Obj) then
        Result := S_OK
      else
        Result := E_NOINTERFACE;
    end;
    
    function TSingletonImplementation._AddRef: Integer;
    begin
      Result := -1;
    end;
    
    function TSingletonImplementation._Release: Integer;
    begin
      Result := -1;
    end;
    
    end.
    

    –jeroen

  4. I can’t believe that nobody has yet mentioned that the biggest danger here is the intention to mix reference counted objects with objects with an explicit managed lifetime.

    That is fine as far as it goes, but the compiler will inject AddRef() and Release() calls around code that obtains and relinquishes interface references, whether or not the implementing object wants to be reference counted.

    If you Free and object that has outstanding interface references to it you will get AV’s when those references go out of scope, even tho you have no code making explicit calls to the object involved!!

    You will sweat blood trying to debug that sort of mess!!!

    This is why it is essentially “safe” on a singleton (which is presumably intended to be explicitly destroyed when the process terminates, when all other references have – or should have – dropped out of scope), but is why applying the technique more generally is A Bad Idea™ unless you know what you are doing.

    And if you know what you are doing you can (and probably already have) implement IUnknown yourself.

    🙂

    • There is no intentention to mix reference counted objects with explicitly managed objects, so I don’t see a danger here.

      A real danger is to do without knowing what you are doing. 🙂

  5. @jpluimers & Mikael Eriksson : thanks for TSingletonImplementation from Generics.Defaults remark, still I’d prefer that the basic functionality was implemented in TObject (just methods to be inherited by descendant classes implementing interfaces, TObject itself should not excibit IUnknown)

  6. @Macedonczyk

    Hi.

    Interesting topic about problems mixing objects with references.

    I’m often use the following approach and I never get AV. I’m not sure if this is wrong or not, but works well for me. I also not sure what happens internally. Using Delphi 2007

    First, subclass TValueObject from TInterfacedObject instead from TObject. TInterfacedObject implements the methods of IInterface, so I don’t need implement those by myself.

    procedure WorksWithOneInterface;
    Var
       aVal: IValue;
       ObjectVal: TValueObject;
    
    begin
       ObjectVal:= TValueObject.Create(11);
       try
          If not supports(objectVal, IValue, aVal) THEN // just for extra care. 
             raise exception.create('The TValueObject doesn't support IValue interface');
          ShowMessage('value: ' + IntToStr(aVal.GetValue); // use aVal
       finally
          FreeAndNil(ObjectVal);
       end;
    end; // I'd never got AV. 
    

    I been just lucky for not get AV?

    • Seems you don’t paid much attention to what was said.

      TInterfacedObject implements reference counting on interfaces references, what is we want to avoid here… We look for options to use interfaces without reference counting and without having to create stubs for IUnknown methods.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s