Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Passing C++ object as IDispatch*

120 views
Skip to first unread message

Tim Whittington

unread,
Oct 30, 2003, 6:02:13 AM10/30/03
to
Hi all

I'm attempting to write some C++ wrappers on top of a set of existing COM
interfaces.
One of these COM interfaces has a method like:

HRESULT X( [in] IDispatch* callback, [out,retval] long coupon )

The callback interface is used to notify the client application (me) when
changes occur on a server.
It's also possible that someone will use IUnknown to bind to another
interface implemented by the object, so it needs to implement another
interface or two as well - i.e. I need to provide an IUnknown, ISomething
implementation as well.

I've got no problems creating and instantiating COM components in VC using
ATL, smart pointers etc. - I'm even willing to hand code the COM
implementation if that's the only way to do it.

The problem I have is that I need to receive the callbacks from the callback
interface and map them back into the C++ application.
So effectively I need to instantiate whatever object implements the
IDispatch interface in C++, passing it a reference to a C++ object that will
deal with the callbacks coming in from COM.
i.e. something like

C++ object <------ C++ method call ---- C++/COM proxy object <---- COM
method call

I can create an object implementing the required interfaces with ATL, but I
can't instantiate it in C++ (i.e. as a vanilla C++ object, not using
CoCreateInstance) - it's technically abstract according to the compiler as
it's missing the actual IDispatch implementation.
I can do this in VB with a class module implementing the required interfaces

Is there any way to achieve this? And if so where should I be looking.

I've searched the net and MSDN for a couple of days now, but have hit a bit
of a brick wall.
Any help would be greatly appreciated.

cheers
tim


Tim Whittington

unread,
Oct 30, 2003, 8:51:22 AM10/30/03
to
OK, this could be a case of RTFM - once I gave up on MFC and had a harder
look at ATL it all became a bit simpler.

I've managed to get this working, but wouldn't mind a more experienced eye
having a look.

I've implemented my class CCallback using ATL (source at end of post), and
then instantiated it as below

HRESULT X( IDispatch** cb )
{
CComClass<CCallback>* pCb;
HRESULT hr = CComClass<CCallback>::CreateInstance( &pCb );
if( FAILED( hr ) ) { /* Snip */ }

pCb->AddRef();
pCb->setThing( pCb ); // This is just to test I can actually set
something inside the C++ object without going through COM

*cb = reinterpret_cast<IDispatch*>(pCb);
}

I'm assuming that it is the clients responsibility to do the Release on the
reference when they're done with it - is this correct?
Are there any other forseeable problems with this approach - notably I
haven't implemented any of the coclass or registration parts :
DECLARE_REGISTRY_RESOURCEID, OBJECT_ENTRY_AUTO, : public CComCoClass
etc - is any of this necessary if I'm only going to instantiate the object
from C++?

Is it cosher to destroy the object from the C++ side, or can I leave it to
COM to destroy it when ->Release() is called?

The other thing that interests me is that I can use the interface through
IDispatch and IUnknown.
For example a JScript client can obtain the callback reference and call the
Ping method - through IDispatch.
I can also use VB to obtain the callback reference, typed to an interface
(from the typelib) and call the Ping method - I'm guessing this is through
using IUnknown/QueryInterface.

I tried to do the same thing using an IUnknown**, but neither JScript (as
expected) or VB would talk using this method.
I would have expected VB to be able to use the IUnknown to get the
interface, but it seems not to work.
Is it just using the interface typing just for IntelliSense and trying to
use IDispatch for all the calls?

Thanks in advance for any help.

tim

------------------- Class source -----------------

class ATL_NO_VTABLE CCOMCP2 :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<ICallback, &__uuidof(ICallback),
&LIBID_CallbackLib, /* wMajor = */ 1, /* wMinor = */ 0>
{
public:
CCallback()
{

}

BEGIN_COM_MAP(CCOMCP2)
COM_INTERFACE_ENTRY(CCallback)
COM_INTERFACE_ENTRY2(IDispatch, CCallback)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

HRESULT FinalConstruct()
{
return S_OK;
}

void FinalRelease()
{
}

void setThing( CComObject<CCallback>* cm )
{
AfxMessageBox( "Got the thing" );
}

public:

// ICallback Methods
public:
STDMETHOD(Ping)()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

AfxMessageBox( "Ping" );
return S_OK;
}

};


"Tim Whittington" <ti...@ihug.co.nz> wrote in message
news:%23ozsOVt...@TK2MSFTNGP11.phx.gbl...

Kim Gräsman

unread,
Oct 30, 2003, 9:11:52 AM10/30/03
to
Hi Tim,

> HRESULT X( IDispatch** cb )
> {
> CComClass<CCallback>* pCb;
> HRESULT hr = CComClass<CCallback>::CreateInstance( &pCb );
> if( FAILED( hr ) ) { /* Snip */ }
>
> pCb->AddRef();
> pCb->setThing( pCb ); // This is just to test I can actually set
> something inside the C++ object without going through COM
>
> *cb = reinterpret_cast<IDispatch*>(pCb);
> }

I would prefer this:

pCb->AddRef();
pCb->setThing( pCb );

hr = pCB->QueryInterface(cb);

pCB->Release();

return hr;

> Is it cosher to destroy the object from the C++ side, or can I leave it to
> COM to destroy it when ->Release() is called?

I'm not sure what you mean... You're not destroying anything in X, right?
Since COM objects are refcounted, the last Release call will kill it.
QueryInterface will bump up refcount by one.

> I would have expected VB to be able to use the IUnknown to get the
> interface, but it seems not to work.
> Is it just using the interface typing just for IntelliSense and trying to
> use IDispatch for all the calls?

VB doesn't know what to QI for in this scenario - you're passing out an
IUnknown, it says IUnknown in the method signature, VB can't know that Ping
is a part of your dual interface (as I guess it is).

All in all, this looks good to me.

--
Best regards,
Kim Gräsman


Tim Whittington

unread,
Oct 30, 2003, 9:37:30 AM10/30/03
to
Thanks for the reply Kim

> I would prefer this:
>
> pCb->AddRef();
> pCb->setThing( pCb );
>
> hr = pCB->QueryInterface(cb);
>
> pCB->Release();
>
> return hr;

I'm assuming the QueryInterface is mostly a style thing, though I can see
it's more explicit even if I do know that the object implements IDispatch.
Is there any practical difference in using this method?

> > Is it cosher to destroy the object from the C++ side, or can I leave it
to
> > COM to destroy it when ->Release() is called?
>
> I'm not sure what you mean... You're not destroying anything in X, right?
> Since COM objects are refcounted, the last Release call will kill it.
> QueryInterface will bump up refcount by one.

What I was getting at was I wasn't sure whether the Release would reclaim
the C++ object - too many years in Java land and the resulting paranoia.
I've had a look at the Release implementation and I'm more reassured now.

> > I would have expected VB to be able to use the IUnknown to get the
> > interface, but it seems not to work.
> > Is it just using the interface typing just for IntelliSense and trying
to
> > use IDispatch for all the calls?
>
> VB doesn't know what to QI for in this scenario - you're passing out an
> IUnknown, it says IUnknown in the method signature, VB can't know that
Ping
> is a part of your dual interface (as I guess it is).

In the VB code I have something like

Dim x as MyLib.ICallback
Set x = something.getCallbackInterface
x.Ping

In this case I would have expected VB to try and QueryInterface the result
of getCallbackInterface to get a reference to ICallback.
It didn't seem to have worked.
Actually sanity checking before I sent this post reveals that it does now
work - it may have been something to do with some dodgy class/interface
registrations.
The VB above and ...

Dim x as Object
Set x = something.getCallbackInterface
x.Ping

... both work now, so it appears that VB is using both IUnknown and
IDispatch or just IDispatch always (though it's getting the IDispatch from
the IUnknown if it is).

I'm feeling a whole bunch more optimistic now :)

cheers
tim

"Kim Gräsman" <k...@mvps.org> wrote in message
news:eYpTG$unDHA...@TK2MSFTNGP12.phx.gbl...

Tim Whittington

unread,
Oct 30, 2003, 9:57:31 AM10/30/03
to
A bit of a newbie question this one:

> I would prefer this:
>
> pCb->AddRef();
> pCb->setThing( pCb );
>
> hr = pCB->QueryInterface(cb);
>
> pCB->Release();
>
> return hr;

Is the object pointed to by cb still the actual C++ object that pCb was
pointing to?

This is important as I need to store within the newly instantiated COM
object a reference to a C++ object that it can use to talk back to the C++
side of the application.

i.e.

STDMETHOD(raw_Ping)()
{
m_pCppObject->Ping();
}


tim


Kim Gräsman

unread,
Oct 30, 2003, 10:36:18 AM10/30/03
to
Tim,

> Is the object pointed to by cb still the actual C++ object that pCb was
> pointing to?

Sure, just through another pointer. That's the difference between interface
and implementation.

Kim Gräsman

unread,
Oct 30, 2003, 10:35:32 AM10/30/03
to
Hi Tim,

> I'm assuming the QueryInterface is mostly a style thing, though I can see
> it's more explicit even if I do know that the object implements IDispatch.
> Is there any practical difference in using this method?

Yeah, and reinterpret_cast is a sure way to trash pointers when used with
multiple inheritance. Always prefer QueryInterface.

> What I was getting at was I wasn't sure whether the Release would reclaim
> the C++ object - too many years in Java land and the resulting paranoia.
> I've had a look at the Release implementation and I'm more reassured now.

Hehe :) Release reclaims _when reference count reaches zero_, as I'm sure
you figured out.

> Dim x as MyLib.ICallback
> Set x = something.getCallbackInterface
> x.Ping
>
> In this case I would have expected VB to try and QueryInterface the result
> of getCallbackInterface to get a reference to ICallback.
> It didn't seem to have worked.
> Actually sanity checking before I sent this post reveals that it does now
> work

Yeah, that should work. VB knows to QI for ICallback here, whereas scripting
clients never will, since they only talk IDispatch.

0 new messages