Board index » delphi » Problem with reference counting aggregated Com objects

Problem with reference counting aggregated Com objects

I have a set of com objects that aggregate other com objects using COM
aggregation (ie, passing a controlling IUnknown to CoCreateInstance,
requesting an IUnknown, etc.).  Certain of the methods Within the aggregated
object pass SELF to a method in another COM object that takes a reference to
one of the aggregated interfaces.  That other object stores the interface
reference in a private field.

When assigning the interface reference, delphi's automatic reference
counting causes any object already referenced by the private field is
_Released, but the new interface assigned to the field does not seem to be
_AddRefed.  Since this is an aggregated object, the _Release calls are
delegated to the controlling object.  After calling the method enough times,
all the references get released, and the object gets unexpectedly destroyed,
even though there should still be many references to it held by other
objects.

I can see the aggregation mechanism is working because_Release is getting
correctly delegated, and I can stop on a breakpoint in the controlling
object's destructor when the refcount reaches 0.  I don't know what is
happening to the implicit _AddRef, and because this object is decended from
TInterfacedObject, which is defined in the System unit, it is rather
difficult to trace through the reference counting logic. But I suspect that
Delphi's aggregation logic is getting confused by the fact that the
aggregated object is passing SELF to a method that takes an interface
reference, possibly bypassing the delegation of the call to _AddRef, or
perhaps implicitly calling IUnknown._AddRef, which is only supposed to be
called by the Controller.

Has any one else had experience with COM aggregation?  Has anyone
experienced behavior like this?  Is there a work-around?

Thanks,
Stuart

 

Re:Problem with reference counting aggregated Com objects


Hi Stuart,

<inline>

Quote
Stuart C. Naifeh wrote in message <795os9$en...@forums.borland.com>...
>I can see the aggregation mechanism is working because_Release is getting
>correctly delegated, and I can stop on a breakpoint in the controlling
>object's destructor when the refcount reaches 0.  I don't know what is
>happening to the implicit _AddRef, and because this object is decended from
>TInterfacedObject, which is defined in the System unit, it is rather
>difficult to trace through the reference counting logic.

Wow! If I understand you correctrly, your aggregated object is derived from
TInterfacedObject ? If so, it's wrong way, because TInterfacedObject isn't
support aggregation at all. Objects that might be aggregatable must have a
dula IUnknown (two IUnknown implementation). First IUnknown (the real one)
deal with objects refcount, but the second one do delegation to controling
IUnknown. First IUnknown if passed back to controler when object is created,
all others IUnknown stuff (remember, is there in all interfaces) is delegated.
See TCOMObject source for details.

Quote
>But I suspect that
>Delphi's aggregation logic is getting confused by the fact that the
>aggregated object is passing SELF to a method that takes an interface
>reference, possibly bypassing the delegation of the call to _AddRef, or
>perhaps implicitly calling IUnknown._AddRef, which is only supposed to be
>called by the Controller.

Thats may be another point. I'm not surre, but Delphi compiler might be
confused with mapping SELF to interface (it's solved in compile time, not in
runtime) and map it to real IUnknown (see above).

Quote
>Has any one else had experience with COM aggregation?  Has anyone
>experienced behavior like this?  Is there a work-around?

I guess that there is a simple clear solution. I hope my comments bring some
light to your problem, but my english skill aren't good enough for better
answer.

HTH

Re:Problem with reference counting aggregated Com objects


Quote
>Wow! If I understand you correctrly, your aggregated object is derived from
>TInterfacedObject ?

No, the aggregated object is derived from TComObject. It's the controller
that is derived from TInterfacedObject.  But I tested changing it to
TComObject and still had the same problem.

Quote
>Thats may be another point. I'm not surre, but Delphi compiler might be
>confused with mapping SELF to interface (it's solved in compile time, not
in
>runtime) and map it to real IUnknown (see above).

That's what I'm thinking might be happening.  I'm still trying to figure out
exactly where the reference count is being incorrectly decremented.

Thanks for your help.

Regards,
Stuart

Re:Problem with reference counting aggregated Com objects


Hi,

see below

Quote
Stuart C. Naifeh wrote in message <798e13$he...@forums.borland.com>...
>>Thats may be another point. I'm not surre, but Delphi compiler might be
>>confused with mapping SELF to interface (it's solved in compile time, not
>in
>>runtime) and map it to real IUnknown (see above).

>That's what I'm thinking might be happening.  I'm still trying to figure out
>exactly where the reference count is being incorrectly decremented.

Ok, try pass down another interface, instead IUnknown.

HTH

Re:Problem with reference counting aggregated Com objects


Hello,
Do you have a demo project? I just tried this and it works fine for me.
--
Binh Ly
Brickhouse Data Systems, Inc.
http://www.brickhouse.com
Quote
Stuart C. Naifeh wrote in message <795os9$en...@forums.borland.com>...
>I have a set of com objects that aggregate other com objects using COM
>aggregation (ie, passing a controlling IUnknown to CoCreateInstance,
>requesting an IUnknown, etc.).  Certain of the methods Within the
aggregated
>object pass SELF to a method in another COM object that takes a reference
to
>one of the aggregated interfaces.  That other object stores the interface
>reference in a private field.

>When assigning the interface reference, delphi's automatic reference
>counting causes any object already referenced by the private field is
>_Released, but the new interface assigned to the field does not seem to be
>_AddRefed.  Since this is an aggregated object, the _Release calls are
>delegated to the controlling object.  After calling the method enough
times,
>all the references get released, and the object gets unexpectedly
destroyed,
>even though there should still be many references to it held by other
>objects.

>I can see the aggregation mechanism is working because_Release is getting
>correctly delegated, and I can stop on a breakpoint in the controlling
>object's destructor when the refcount reaches 0.  I don't know what is
>happening to the implicit _AddRef, and because this object is decended from
>TInterfacedObject, which is defined in the System unit, it is rather
>difficult to trace through the reference counting logic. But I suspect that
>Delphi's aggregation logic is getting confused by the fact that the
>aggregated object is passing SELF to a method that takes an interface
>reference, possibly bypassing the delegation of the call to _AddRef, or
>perhaps implicitly calling IUnknown._AddRef, which is only supposed to be
>called by the Controller.

>Has any one else had experience with COM aggregation?  Has anyone
>experienced behavior like this?  Is there a work-around?

>Thanks,
>Stuart

Re:Problem with reference counting aggregated Com objects


Quote
Stuart C. Naifeh wrote in message <798e13$he...@forums.borland.com>...
>No, the aggregated object is derived from TComObject. It's the controller
>that is derived from TInterfacedObject.  But I tested changing it to
>TComObject and still had the same problem.

AFAIK, the aggregated object must be derived from... well, TAggregatedObject
or TContainedObject (still haven't figured out the difference, I never used
them so far).

Mark

Re:Problem with reference counting aggregated Com objects


Binh,

I've pinpointed the place that causes my error, and I have a test project
that I'll list below. The project creates and aggregator class based on a
do-nothing IDispatch interface and an aggregated interface from another
object.  .Basically, any COM object that supports aggregation can be used as
the aggregated object.

The meat of the test is in the method TForm1.DoTest. The two casts performed
in this method seem to decrement the refcount on the aggregator object one
time too many.  I think it may have something to do with passing the
interface by reference, but it's not clear to me that I've done anything
wrong here. On the third call to this method, all the outstanding references
to the object have been released, and the object is destroyed, causing an
access violation. Code in the Button1Click method shows the falling
reference count in two TLabels.

Thanks for any insight you might have.

Regards,
Stuart

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ActiveX, ComObj, BucketScheduler_TLB, StdCtrls, PROJECT1_TLB;

type
  TTestWC = class(TAutoObject,IMyInterface,IWorkCenter)
  private
    FWorkCenterUnk: IUnknown;
    FWorkCenter: IWorkCenter;
    FText: string;
    property WorkCenter: IWorkCenter read FWorkCenter implements
IWorkCenter;
  protected
  {IMyInterface}
    procedure SomeMethod; safecall;
  public
    procedure Initialize; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel; {References before}
    Label2: TLabel; {References after}
    Label3: TLabel;
    Label4: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FWC: TTestWC;
    procedure DoTest( var WC: IWorkCenter; var TestOK: Boolean );
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses
  ComServ;

{ TTestWC }

procedure TTestWC.Initialize;
begin
  inherited Initialize;
  OleCheck( CoCreateInstance( CLASS_WorkCenter, self, CLSCTX_INPROC_SERVER,
IUnknown, FWorkCenterUnk ) );
  FWorkCenter := FWorkCenterUnk as IWorkCenter;
  FText := 'This is a test';
end;

procedure TTestWC.SomeMethod;
begin
  ShowMessage( FText );
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  FWC := TTestWC.Create;
  FWC._AddRef; { make sure there is initially one reference to this object }
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  WC: IWorkCenter;
  B: Boolean;
begin
  Label3.Caption := IntToStr( FWC.RefCount );
  WC := FWC;
  B := true;
  DoTest( WC, B );
  WC := nil;
  Label4.Caption := IntToStr( FWC.RefCount );
end;

procedure TForm1.DoTest(var WC: IWorkCenter; var TestOK: Boolean);
var
  I: IMyInterface;
begin
  I := WC as IMyInterface;

  I.SomeMethod; { This call forces the AV once the object has been
destroyed}
  {Do something with I that might change its value - then pass the changed
value back to the caller as an IWorkCenter }

  WC := I as IWorkCenter; { this construction seems to call _Release one too
many times}
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  { Kill our reference to this object }
  FWC._Release;
end;

initialization
  TAutoObjectFactory.Create( ComServer, TTestWC, CLASS_TestWC, ciInternal,
tmApartment );
end.

Other Threads