Google 網路論壇不再支援新的 Usenet 貼文或訂閱項目,但過往內容仍可供查看。

"implements" bug still exists?

瀏覽次數:0 次
跳到第一則未讀訊息

Gary Filipski

未讀,
1999年11月10日 凌晨3:00:001999/11/10
收件者:

Hello All,

In Delphi 4, back before patch 2 there was a reported bug with the
"implements" keyword when used with an object -- it seems there was an
extra "addref" done. This was reported fixed in patch #2:
Area: compiler\delphi\interfaces
Reference Number: 1087 (Published: 10/28/98)
Status: Fixed in Version 4 Update Pack 2
Date Reported: 8/6/98
Problem:
Using the Implements property on a class results in

an additional AddRef in either the delegating or
delegated object.

I wonder.

The following two code snippets do the same thing (see the very end of
this message) ... ie, backs up to the parent node of a tree and then
adds a node to the tree as a sibling of the old "current" node. The
code works fine and I don't have any question about it doing the right
thing ... except that if I code it acording to "option 1" below it
works fine. If I code it acording to "option 2" then I end up with an
extra reference count where there's supposed to be a reference count
of no more than one (in a contained object). The point here isn't
what the code does, but that coding it with an intermediate temporary
variable instead of the "natural" way causes different results. I've
looked at the generated code and can't find any functional difference
in things, but I'm a novice assembly guy and would probably miss a
subtlety.

The short story of what happens in the code below is as follows - this
is as simple as I could make it.

There is a TTreeNodeStructure object that implements a set of tree
operations. This is derived from a TAggregatedObject and is intended
to be used as the target of an "implements". The object that actually
makes up the tree node is derived from TContainedCOMObject. The
TContainedCOMObject class "fixes" the QueryInterface call for what
would normally be an aggregated COM object so that the query is back
to the original object's QueryInterface. This "Contained" COM Object
uses the TTreeNodeStructure object mentioned above (derived from
TAggregatedObject) to implement a set of tree operations for this
contained object. Depending on whether I code "option 1" or "option
2" below the program will run fine or refuse to terminate because COM
objects are still in existence. There are no other changes in the
source. UI actions are identical. Turning off optimization has no
effect.

What's happening here? Am I looking at a bonafied compiler bug or do
I have my COM technology all screwed up... I'm grasping at straws
here.

Thanks,
Gary

Gary L. Filipski
fili...@satx.meitech.com
Mei Technology Company Tel: (210)655-8911
8930 Fourwinds Dr., Suite #450 Fax: (210)590-3338
San Antonio, Texas 78239


type
TContainedCOMObject = class( TCOMObject
, IUnknown
, ISupportErrorInfo
)

{ TContainedCOMObject Object - Implementation of revised
QueryInterface to support contained object functionality }
protected
// Our IUnknown interface calls
function IUnknown.QueryInterface = ObjQueryInterface;
function IUnknown._AddRef = ObjAddRef;
function IUnknown._Release = ObjRelease;

// All the other interfaces' IUnknown calls
function QueryInterface( const IID: TGUID;
out Obj) : HResult; stdcall;
// The above is the reason we had to "re-implement" all the interfaces
// that TCOMObject supports. Even though we probably didn't *have*
// to do every one of them... better safe than sorry.
end;


ITreeNodeStructure = interface(IUnknown) ['...']
function Get_Parent : ITreeNodeStructure; safecall;
procedure Set_Parent( const pValue: ITreeNodeStructure );
safecall;
... all safecall methods ...
end;

TTreeNodeStructure = class(TAggregatedObject)
function Get_Parent : ITreeNodeStructure; safecall;
procedure Set_Parent( const pValue: ITreeNodeStructure );
safecall;
... all safecall methods ...
end;

IMyDataNode = interface(IUnknown) ['...']
... all safecall methods ...
end;

TMyDataNode = class( TContainedCOMObject
, IMyDataNode
, ITreeNodeStructure )

public
procedure Initialize; override;
destructor Destroy; override;

{ TMyDataNode: IMyDataNode }
... Stuf to implement IMyDataNode ...

{ TMyDataNode: ITreeNodeStructure }
private
fTNS: TTreeNodeStructure;

protected
property TNS: TTreeNodeStructure
read fTNS implements ITreeNodeStructure;

end;

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


... sometime later in a Form's Event procedure ....

procedure ...
var
vTreeNode: ITreeNodeStructure;
begin

(* Option 1 -- this behaves correctly *)
vTreeNode := (uNode as ITreeNodeStructure).Parent;
uNode := vTreeNode.
AddNodeOfClass( CLASS_MyDataNode ) as IMyDataNode;

(* Option 2 -- this leaves an extra reference count in what used
to be the node pointed to by uNode, IE, the object that actually
exercised the "implements" to get to ".Parent" *)
uNode := (uNode as ITreeNodeStructure).Parent.
AddNodeOfClass( CLASS_MyDataNode ) as IMyDataNode;

end;

Gary Filipski

未讀,
1999年11月11日 凌晨3:00:001999/11/11
收件者:
On Wed, 10 Nov 1999 14:02:58 -0600, Gary Filipski
<fili...@satx.meitech.com> wrote:

>
>Hello All,
>
>In Delphi 4, back before patch 2 there was a reported bug with the
>"implements" keyword when used with an object -- it seems there was an
>extra "addref" done. This was reported fixed in patch #2:
> Area: compiler\delphi\interfaces
> Reference Number: 1087 (Published: 10/28/98)
> Status: Fixed in Version 4 Update Pack 2
> Date Reported: 8/6/98
> Problem:
> Using the Implements property on a class results in
> an additional AddRef in either the delegating or
> delegated object.
>
>I wonder.
>

Ok, I no longer wonder. The bug is not in the "implements" keyword
but in forcing the compiler to create a temporary interface variable.
It apparently forgets to "release" the interface that it stored in the
temporary. It took a while, but I finally built a test program to
demo it.... with none of my non-standard COM flailings involved.

IE. If you do something like:

vSomeIntf := (vSomeVar as ISomeOther).IntfRef.XXX as ISomeIntf;

That is, for the compiler to create temporary storage for an interface
then it fails to do the release. If you write the above as:

var vTemp: ISomeOther;

vTemp := vSomeVar as ISomeOther;
vSomeIntf := vTemp.IntRef.XXX as ISomeIntf;

... then it works fine.

This bug may or may not have been reported in Delphi 5, but it wasn't
in 4. This can make coding really awkward.

0 則新訊息