实现
先看IDispatch接口,如同反射
interface IDispatch : IUnknown {
HRESULT GetTypeInfoCount([out] UINT * pctinfo);
HRESULT GetTypeInfo([in] UINT iTInfo,
[in] LCID lcid,
[out] ITypeInfo ** ppTInfo);
HRESULT GetIDsOfNames([in] REFIID riid,
[in, size_is(cNames)] LPOLESTR * rgszNames,
[in] UINT cNames,
[in] LCID lcid,
[out, size_is(cNames)] DISPID * rgDispId);
HRESULT Invoke([in] DISPID dispIdMember,
[in] REFIID riid,
[in] LCID lcid,
[in] WORD wFlags,
[in, out] DISPPARAMS * pDispParams,
[out] VARIANT * pVarResult,
[out] EXCEPINFO * pExcepInfo,
[out] UINT * puArgErr);
}
要实现IDispatch接口的动作是差不多的
class CPenguin :
public CComObectRootEx<CComSingleThreadModel>,
public IBird,
public ISnappyDresser,
public IPenguin {
public:
CPenguin() : m_pTypeInfo(0) {
IID* pIID = &IID_IPenguin;
GUID* pLIBID = &LIBID_BIRDSERVERLib;
WORD wMajor = 1;
WORD wMinor = 0;
ITypeLib* ptl = 0;
HRESULT hr = LoadRegTypeLib(*pLIBID, wMajor, wMinor,
0, &ptl);
if( SUCCEEDED(hr) ) {
hr = ptl->GetTypeInfoOfGuid(*pIID, &m_pTypeInfo);
ptl->Release();
}
}
virtual ~Penguin() {
if( m_pTypeInfo ) m_pTypeInfo->Release();
}
BEGIN_COM_MAP(CPenguin)
COM_INTERFACE_ENTRY(IBird)
COM_INTERFACE_ENTRY(ISnappyDresser)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IPenguin)
END_COM_MAP()
// IDispatch methods
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) {
return (*pctinfo = 1), S_OK;
}
STDMETHODIMP GetTypeInfo(UINT ctinfo, LCID lcid,
ITypeInfo **ppti) {
if( ctinfo != 0 ) return (*ppti = 0), DISP_E_BADINDEX;
return (*ppti = m_pTypeInfo)->AddRef(), S_OK;
}
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames,
UINT cNames, LCID lcid, DISPID *rgdispid) {
return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames,
rgdispid);
}
STDMETHODIMP Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pdispparams,
VARIANT *pvarResult,
EXCEPINFO *pexcepinfo,
UINT *puArgErr) {
return m_pTypeInfo->Invoke(static_cast<IPenguin*>(this),
dispidMember, wFlags,
pdispparams, pvarResult,
pexcepinfo, puArgErr);
}
// IBird, ISnappyDresser and IPenguin methods...
private:
ITypeInfo* m_pTypeInfo;
};
以IDispatchImpl替代
class CPenguin :
public CComObjectRootEx<CComMultiThreadModel>,
public IBird,
public ISnappyDresser,
public IDispatchImpl<IPenguin, &IID_IPenguin> {
public:
BEGIN_COM_MAP(CPenguin)
COM_INTERFACE_ENTRY(IBird)
COM_INTERFACE_ENTRY(ISnappyDresser)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IPenguin)
END_COM_MAP()
// IBird, ISnappyDresser and IPenguin methods...
};
IDispatchImpl具体实现
template <class T, const IID* piid = &__uuidof(T), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispatchImpl :
public T
{
public:
typedef tihclass _tihclass;
// IDispatch
STDMETHOD(GetTypeInfoCount)(_Out_ UINT* pctinfo)
{
if (pctinfo == NULL)
return E_POINTER;
*pctinfo = 1;
return S_OK;
}
STDMETHOD(GetTypeInfo)(
_In_ UINT itinfo,
_In_ LCID lcid,
_Deref_out_ ITypeInfo** pptinfo)
{
return _tih.GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHOD(GetIDsOfNames)(
_In_ REFIID riid,
_In_count_(cNames) _Deref_pre_z_ LPOLESTR* rgszNames,
_In_ UINT cNames,
_In_ LCID lcid,
_Out_ DISPID* rgdispid)
{
return _tih.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}
STDMETHOD(Invoke)(
_In_ DISPID dispidMember,
_In_ REFIID riid,
_In_ LCID lcid,
_In_ WORD wFlags,
_In_ DISPPARAMS* pdispparams,
_Out_opt_ VARIANT* pvarResult,
_Out_opt_ EXCEPINFO* pexcepinfo,
_Out_opt_ UINT* puArgErr)
{
return _tih.Invoke((IDispatch*)this, dispidMember, riid, lcid,
wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
}
#ifdef _ATL_DLL_IMPL
// Do not cache type info if it is used in the ATL dll
IDispatchImpl() : _tih(piid, plibid, wMajor, wMinor)
{
}
virtual ~IDispatchImpl()
{
}
protected:
_tihclass _tih;
HRESULT GetTI(
_In_ LCID lcid,
_Deref_out_ ITypeInfo** ppInfo)
{
return _tih.GetTI(lcid, ppInfo);
}
#else
protected:
static _tihclass _tih;
static HRESULT GetTI(
_In_ LCID lcid,
_Deref_out_ ITypeInfo** ppInfo)
{
return _tih.GetTI(lcid, ppInfo);
}
#endif
};
调用IDispatch接口
很麻烦的一个过程,如下动态调用Add方法
// component IDL file
[
object,
uuid(2F6C88D7-C2BF-4933-81FA-3FBAFC3FC34B),
dual,
]
interface ICalc : IDispatch {
[id(1)] HRESULT Add([in] DOUBLE Op1,
[in] DOUBLE Op2, [out,retval] DOUBLE* Result);
};
// client.cpp
HRESULT CallAdd(IDispatch* pdisp) {
// Get the DISPID
LPOLESTR pszMethod = OLESTR("Add");
DISPID dispid;
hr = pdisp->GetIDsOfNames(IID_NULL,
&pszMethod,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
if (FAILED(hr))
return hr;
// Set up the parameters
DISPPARAMS dispparms;
memset(&dispparms, 0, sizeof(DISPPARAMS));
dispparms.cArgs = 2;
// Parameters are passed right to left
VARIANTARG rgvarg[2];
rgvarg[0].vt = VT_R8;
rgvarg[0].dblVal = 6;
rgvarg[1].vt = VT_R8;
rgvarg[1].dblVal = 7;
dispparms.rgvarg = &rgvarg[0];
// Set up variable to hold method return value
VARIANTARG vaResult;
::VariantInit(&vaResult);
// Invoke the method
hr = pdisp->Invoke(dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&dispparms,
&vaResult,
NULL,
NULL);
// vaResult now holds sum of 6 and 7
}
CComPtr<IDispatch>提供了很多的便利方法
调用如下:
HRESULT TheEasyWay( IDispatch *spCalcDisp ) {
CComPtr< IDispatch > spCalcDisp( pCalcDisp );
CComVariant varOp1( 6.0 );
CComVariant varOp2( 7.0 );
CComVariant varResult;
HRESULT hr = spCalcDisp.Invoke2( OLESTR( "Add" ),
&varOp1, &varOp2, &varResult );
// varResult now holds sum of 6 and 7
}