Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: TComInterface operator& overly restrictive


This question is not answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 1 - Last Post: Jun 26, 2014 6:39 PM Last Post By: Remy Lebeau (Te...
Matt McNabb

Posts: 21
Registered: 6/9/11
TComInterface operator& overly restrictive  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 26, 2014 5:44 PM
Using C++Builder XE5. Calling a function which I have defined as (in IDL):
      HRESULT _stdcall update_myobject([in] long a_number, [in, out] IMyObject** myobject);

It takes a MyObject which has already been created, and calls some functions on it.

To call this function from a C++Builder host, I tried using the auto-generated TComInterface wrapper from MyObject_TLB.h:
    TCOMIMyObject iPtr = CoMyObject::Create();
    HRESULT hr = iPtr->update_myobject(1234, &iPtr);

However this causes an assertion failure in Debug Mode because the definition of TComInterface<T>::operator& in utilcls.h is:
    // NOTE: You must explicitly Reset() any held interface pointers before invoking
    //       the &operator (presumably to store another interface in the object)
    // (Should we do the Release() automatically and eliminate the _ASSERTE_ check??)
    //
    T** operator & ()
    {
      _ASSERTE_(intf==0 /* Don't allow &() of smart interface with NULL pointer interface */);
      return &intf;
    }

It works correctly in Release Mode , when the assert is not checked.

This operator& would work if I were using it with an [out] parameter, expecting to create an object. However, it does not work for an [in,out] parameter. I cannot see any other function in TComInterface which returns T** . So it seems that my only options are:

* Edit utilcls.h to remove this check
* Stop using TComInterface - roll my own RAII holder for an interface pointer.

Am I overlooking anything?

Is this a bug in utilcls.h? (The comment seems bogus - it's copy-pasted from operator-> which checks _ASSERTE_(intf != 0) . The check ENFORCES NULL pointer interface, as opposed to "not allowing" it.)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: TComInterface operator& overly restrictive [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 26, 2014 6:39 PM   in response to: Matt McNabb in response to: Matt McNabb
Matt wrote:

HRESULT hr = iPtr->update_myobject(1234, &iPtr);

You are passing a pointer to the same object that update_myobject() is being
called on. Are you sure that is what you really want? That is pretty dangerous,
if update_myobject() decides to return a new object (which is what [in, out]
is meant for). iPtr has a reference count of 1 when update_myobject() is
called. If update_myobject() calls (*myobject)->Release() before assigning
a new object to *myobject, the calling object's destructor will be invoked
while update_myobject() is still running!

HRESULT _stdcall TMyObjectImpl::update_myobject(long a_number, IMyObject** 
myobject)
{
    ...
    if (need to return a new object)
    {
        ...
        (*myobject)->Release(); // <-- this->~TMyObjectImpl() could be invoked 
here!
        *myobject = ...;
        ...
    }
    ...
}


If you are not VERY careful with that, you could seriously screw things up.
At the very least, this would be safer:

HRESULT _stdcall TMyObjectImpl::update_myobject(long a_number, IMyObject** 
myobject)
{
    ...
    if (need to return a new object)
    {
        ...
        IMyObject *tmp = *myobject;
        *myobject = ...;
        tmp->Release();
        ...
    }
    ...
}


Or:

HRESULT _stdcall TMyObjectImpl::update_myobject(long a_number, IMyObject** 
myobject)
{
    ...
    if (need to return a new object)
    {
        ...
        TCOMIMyObject tmp(*myobject, false);
        *myobject = ...;
        ...
    }
    ...
}


So it seems that my only options are:

There are two more options:

1. Retrieve the T** pointer before you populate the TComInterface, while
the interface pointer is still NULL:

TCOMIMyObject iPtr;
IMyObject **ppObj = &iPtr;
iPtr = CoMyObject::Create();
HRESULT hr = iPtr->update_myobject(1234, ppObj);


2. Since the TComInterface::intf member is declared as protected, you can
derive your own class from TComInterface to expose the T** pointer:

class TMyCOMIMyObject : public TCOMIMyObject
{
public
    IMyObject** IntfAddr() { return &intf; };
};
 
TMyCOMIMyObject iPtr = CoMyObject::Create();
HRESULT hr = iPtr->update_myobject(1234, iPtr.IntfAddr());


Or maybe:

class TMyCOMIMyObject : public TCOMIMyObject
{
public
    using TCOMIMyObject::operator *;
    using TCOMIMyObject::operator ->;
    using TCOMIMyObject::operator =;
    using TCOMIMyObject::operator !;
    using TCOMIMyObject::operator bool();
    ...
    IMyObject** operator&() { return &intf; };
};
 
TMyCOMIMyObject iPtr = CoMyObject::Create();
HRESULT hr = iPtr->update_myobject(1234, &iPtr);


Is this a bug in utilcls.h?

Maybe, maybe not. The '&' operator of ATL's CComPtr class has the same restriction
(or at least it used to, I don't know if it still does), and ATL was written
by Microsoft.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02