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.