The subject of BCB smart pointers has been covered at least once before in
this group - not that I'm blaming you for bringing it up again as I just
searched groups.google.com and couldn't find the posting I made last time!
To recap:
The right thing to do with an interface pointer passed as an input parameter
is to leave it alone or both AddRef and Release it. The COM Specification,
Chaper 2, Reference Counting, Rule 2a says:
"Rule 2a: In-parameters to functions. The copy of an interface pointer which
is passed as an actual parameter to a function has a lifetime which is
nested in that of the pointer used to initialize the value. The actual
parameter therefore need not be separately reference counted."
Although the rule says *need* not, which implies that an AddRef/Release pair
is technically allowed, it is logical that you would add an AddRef and a
Release in the server code AND/OR an AddRef and a Release in the client
code. What BCB actually does is, by using its smart pointers,
is add an AddRef in the client code and a Release in the server code, which
is the root of the problem. This is the reason BCB inserts a warning comment
into the server code for a function with a smart pointer input
parameter.
This smart pointer behaviour works if both client and server are written in
C++Builder but breaks down when one or other is coded in another development
tool (surely one of the main reasons for using COM!). (Incidentally, IMHO
this means Borland have broken the rules of COM, thus it IS a bug.)
My solution, inspired by the compiler generated comment, is to immediately
AddRef any smart pointers inside the BCB server code AND to Release smart
pointers after calling in the client code e.g.
void somefunc()
{
ISmartPtr p = Something;
Foo->Bar(p); // Copy ctor of smart pointer implicitly calls AddRef.
p->Release(); // Balance smart pointer AddRef
}
// Builder's generated code:
interface IFoo:
{
Bar(ISmartPtr p)
{
p->AddRef(); // Balance smart pointer Release.
// Other code.
// Dtor of smart pointer implicitly calls Release.
}
};
Now, if either the client or server code is replaced by non-BCB code, the
interface is still destroyed at the correct point.
The solution is quite {*word*193}, but it works (although see note below). What
would be nicer is a kind of TComInterfaceInParam type, which leaves the
reference count alone on construction and destruction, or, simpler but
without the other advantages of TComInterface, just passing a raw interface
pointer.
Note that for parameters that end up as pointers to TComInterface (e.g.
ISmartPtr*) - typically output parameters - no reference count manipulation
is needed (even though the compiler still spits out its warning comment into
the server code) since the copying of a such a parameter is just the copying
of a raw pointer which does not create a new smart pointer and thus does not
change the reference count.
NOTE: The only occassion it wouldn't work is when Bar returns a failure
HRESULT, which the smart pointer Foo converts into an exception. In that
case, execution will jump to an exception handler, the p.Release() will
never get called and p will leak. Dealing correctly with this situation
requires a catch or (yikes!) __finally block.
Hope this sheds some light.
--
Garry Lancaster
Codemill Ltd
mailto: glancas...@NOSPAMPLEASEcodemill.net
Visit our web site at http://www.codemill.net
Quote
David Bridges <brid...@advpubtech.com> wrote in message
news:3a8ed627$1_1@dnews...
Quote
> I'm not sure why the "smart interface pointers" work the way they do. For
> example, I have the following method in one of my COM objects (CPPB 5.0
> project):
> STDMETHODIMP TMyObjectImpl::Method1(IMyObjectPtr OtherObject, TOLEBOOL*
> Result)
> {
> // OtherObject must have its reference count incremented because
> // it will automatically Release() when it goes out of scope.
> // Why did Borland do this????
> OtherObject->AddRef();
> // Do stuff...
> return S_OK;
> };
> If I don't call AddRef() as shown above, then 'OtherObject' will
> automatically release itself, leading to all kinds of bad problems. My
> desire is to use 'OtherObject' which is another "live" object. It seems as
> though the ATL 'xxxPtr' types will automatically call Release() when they
go
> out of scope - true? I guess this is kind of good (it makes us have to
write
> one less line of code!), but its bad because the resulting code no longer
> has matching calls to AddRef() and Release().
> Assuming I was using a more basic type like LPUNKOWN, I assume I wouldn't
> have this problem. Since I'm using Apartment Threading in my COM library,
> should I still call OtherObject->AddRef() at the beginning of the method
and
> OtherObject->Release() at the end? My guess is: technically - no,
conforming
> to COM guidelines - yes.
> I would appreciate any input on this
> Thanks!