I’m probably jumping into this topic a bit late but it doesn’t seem to end. I
admit I haven’t read all article in this thread but I think I have an
implementation solution for multiple dual interfaces on one class. And I would
like opinions on it from others as well, and as to whether or not it displays
proper COM implementation.
The implementation is very much like one that has been posted earlier, which
I’ve seen different variations of many times before. However, I feel the one I
am presenting allows all dual interfaces to be on one object and accessible via
scripting without explicitly asking for a piece. No new interface definitions
will be necessary with this technique. However, there may be one flaw. It will
work ideally when none of the dispid’s of any of the dual interfaces collide,
and when none of the named functions collide.
I consider a collision when the dispids,or names are equal, but the parameter
list/type are not. Even still, when they do collide, the implementers using
this technique can choose which should take priority.
Using ATL, I defined a templated class which should be derived by the class
that exposes multiple dual interfaces.
template<UINT t_uiNum, class tihclass =
CComTypeInfoHolder>
class XMultiDualImpl : public IDispatch
{
public :
typedef tihclass _tihclass;
void Add( UINT uiIndex, LPDISPATCH pDisp, _tihclass &
tih)
{
ASSERT( t_uiNum > uiIndex-1
&& uiIndex>0 );
m_arrDispImpls[uiIndex-1] = pDisp;
//no need to addref, pDisp==this
m_arrpTih[uiIndex-1] = &tih;
};
STDMETHOD(GetTypeInfoCount)(UINT*
pctinfo) {*pctinfo = 1; return S_OK;}
STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo**
pptinfo)
{return m_arrpTih[0]->GetTypeInfo(itinfo, lcid,
pptinfo);}
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames,
UINT cNames,
LCID lcid, DISPID* rgdispid)
{
/*perhaps the loop should check for
a succeeded hresult instead*/
HRESULT hr = DISP_E_UNKNOWNNAME;
for ( UINT i=0; i < t_uiNum
&& hr == DISP_E_UNKNOWNNAME; i++ )
{
hr =
m_arrpTih[i]->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}
return hr;
};
STDMETHOD(Invoke)(DISPID
dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS*
pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
HRESULT hr = DISP_E_MEMBERNOTFOUND;
for ( int i=0; i < t_uiNum
&& hr == DISP_E_MEMBERNOTFOUND ; i++ )
{
hr =
m_arrpTih[i]->Invoke( m_arrDispImpls[i], dispidMember, riid, lcid, wFlags,
pdispparams, pvarResult, pexcepinfo, puArgErr);
}
return hr;
} ;
LPDISPATCH m_arrDispImpls[t_uiNum];
_tihclass * m_arrpTih[t_uiNum];
};
As an example, if we were to implement an object with 3 dual interfaces we
would do the following
typedef XMultiDualImpl<3> XTriDualImpl ;
class DispTest3 : public XTriDualImpl,
public IDispatchImpl<IDispTest1, &IID_IDispTest1,
&LIBID_FirstLib>,
public IDispatchImpl<IDispTest2, &IID_IDispTest2,
&LIBID_SecondLib>,
public IDispatchImpl<IThirdIntfTest,
&IID_IThirdIntfTest, &LIBID_ThridLib>,
public CComObjectRoot,
public CComCoClass<DispTest3,&CLSID_DispTest3>
{
public:
DispTest3()
{
Add( 1,(IDispTest1*)this,
IDispatchImpl<IDispTest1, &IID_IDispTest1, &LIBID_FirstLib>::_tih
);
Add( 2,(IDispTest2*)this,
IDispatchImpl<IDispTest2, &IID_IDispTest2,
&LIBID_SecondLib>::_tih );
Add( 3,(IThirdIntfTest*)this,
IDispatchImpl<IThirdIntfTest, &IID_IThirdIntfTest,
&LIBID_ThridLib>::_tih );
}
BEGIN_COM_MAP(DispTest3)
COM_INTERFACE_ENTRY2(IDispatch, XTriDualImpl)
COM_INTERFACE_ENTRY(IDispTest1)
COM_INTERFACE_ENTRY(IDispTest2)
COM_INTERFACE_ENTRY(IThirdIntfTest)
END_COM_MAP()
//IDispTest1 methods [code omitted for brevity]
//IDispTest2 methods [code omitted for brevity]
//IThirdIntfTest methods [code omitted for brevity]
};
I admit the constructor of the derived class leaves room for elegance.
Basically, all QueryInterfaces for IDispatch will result in the XMultiDualImpl
vtable. The IDispatch implementation for that vtable basically asks each
IDispatch vtable (of the dual interface) in it’s array one by one if it can
carry out the call. If it fails, it moves on to the next dual interface vtable.
One might point out the following however :
pUnk->QueryInterface( IID_IDispatch,
(void**)&pIDispatch );
pUnk->QueryInteface( IID_IDispTest1, (void**)&pIDispTest1 );
pIDispatch->GetIDsOfNames does not have the same implementation as
pIDispTest1->GetIDsOfNames. as well as the implementation differs for
->Invoke. I question whether or not that’s a problem. Since IDispTest1
derives from IDispatch, I could see why it may be.
-herb