在一般情况下,简单的IDispatch接口我们可以不依赖类型库来完成,但是对于分发函数比较多或是需要具体类型信息的场合,还是需要使用类型库来实现IDispatch接口。
1.通用的依赖类型库实现IDispatch
前面讲了在ATL中使用类型库创建接口的方法,这里以ATL来演示通用的借助类型库实现IDispatch接口的过程。但是实际上只要提供类型库,在MFC中也可以使用此方法。
编辑类型库,声明一个接口继承自IDispatch
[
object,
uuid(CA00179E-7AD8-4C6D-8544-23A571C31D8C),
dual,
nonextensible,
helpstring("ICat 接口"),
pointer_default(unique)
]
interface ICat : IDispatch{
[id(1)] HRESULT SayHello1([in] BSTR szWord);
[id(2)] HRESULT SayHello2([in] int nAge);
};
注意这里声明为双端接口dual,在双端接口下既可通过Invoke调用方法也可以通过直接获取接口调用对应的方法,兼顾了脚本语言的易用性和编译语言的性能。
ATL使用多重继承实现COM,所以声明COM组件对象如下:
class ATL_NO_VTABLE CAnimalObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAnimalObject, &CLSID_AnimalObject>,
public ICat
自定义函数加载类型库,导出对应的信息接口指针
HRESULT CAnimalObject::MyLoadTypeLib(LCID lcid, ITypeInfo **ppTInfo)
{
HRESULT hr=E_FAIL;
ITypeLib *pTypeLib=NULL;
if (ppTInfo==NULL)
{
return E_POINTER;
}
*ppTInfo = NULL;
if (m_pITI==NULL)
{
//加载Type Lib
hr = LoadRegTypeLib(LIBID_AtlDispatchLib, 1, 0, lcid, &pTypeLib);
if (FAILED(hr))
{
hr = LoadTypeLib(OLESTR("AtlDispatch.tlb"), &pTypeLib);
}
if (FAILED(hr))
{
return hr;
}
//导出TypeInfo接口
hr = pTypeLib->GetTypeInfoOfGuid(IID_ICat, &m_pITI);
pTypeLib->Release();
if (FAILED(hr))
{
return hr;
}
}
*ppTInfo = m_pITI;
m_pITI->AddRef();
OutputDebugString(L"类型库加载成功");
return S_OK;
}
存在类型库,所以GetTypeInfoCount实现如下:
HRESULT STDMETHODCALLTYPE CAnimalObject::GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo)
{
*pctinfo = 1; //存在类型库
return S_OK;
}
GetTypeInfo、GetIDsOfNames、Invoke均借助于ITypeInfo接口,如下:
HRESULT STDMETHODCALLTYPE CAnimalObject::GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
{
if (0!=iTInfo)//0表示获取该接口实现的类型信息
{
return TYPE_E_ELEMENTNOTFOUND;
}
return MyLoadTypeLib(lcid, ppTInfo);
}
HRESULT STDMETHODCALLTYPE CAnimalObject::GetIDsOfNames(
/* [in] */ __RPC__in REFIID riid,
/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
/* [range][in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
{
HRESULT hr = E_FAIL;
ITypeInfo* pTypeInfo=NULL;
if (riid!=IID_NULL)//riid为保留参数,必须为IID_NULL
{
return DISP_E_UNKNOWNINTERFACE;
}
//借用类型库完成GetIDsOfNames
hr = GetTypeInfo(0, lcid, &pTypeInfo);
if (SUCCEEDED(hr) && pTypeInfo)
{
hr = pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
pTypeInfo->Release();
}
return hr;
}
HRESULT STDMETHODCALLTYPE CAnimalObject::Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr)
{
HRESULT hr = E_FAIL;
ITypeInfo* pTypeInfo=NULL;
if (riid!=IID_NULL)//riid为保留参数,必须为IID_NULL
{
return DISP_E_UNKNOWNINTERFACE;
}
hr = GetTypeInfo(0, lcid, &pTypeInfo);
if (FAILED(hr) || NULL==pTypeInfo)
{
return hr;
}
//借用类型库完成Invoke
hr = pTypeInfo->Invoke((ICat*)this, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
if (FAILED(hr))
{
pTypeInfo->Release();
return DISP_E_EXCEPTION;
}
pTypeInfo->Release();
return hr;
}
2.ATL中的实现IDispatch方法
ATL实现IDispatch接口非常简单,和上面的同样方法实现一样,只是IDispatch接口实现放在了类IDispatchImpl中,我们只要如下声明即完成了IDispatch接口。
class ATL_NO_VTABLE CAnimalObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAnimalObject, &CLSID_AnimalObject>,
public IDispatchImpl<ICat, &IID_ICat, &LIBID_AtlDispatchLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
是不是非常简单?
实际上,IDispatchImpl做的事情和我们之前的方法一模一样,只是微软做了个封装而已,如果感兴趣可以自行阅读源码。
3.调用IDispatch接口
类似MFC中借助COleDispatchDriver类简化Invoke函数调用,ATL中借助CComDispatchDriver类来简化Invoke函数调用,如下:
//初始化COM库
if (CoInitialize(NULL) != S_OK)
{
wcout << L"Fail to Initialize COM" << endl;
return -1;
}
//自动化调用
CComDispatchDriver d;
if (SUCCEEDED(d.CoCreateInstance(CLSID_AnimalObject)))
{
CComVariant p1;
p1.vt = VT_BSTR;
p1.bstrVal = SysAllocString(L"毛毛");
CComVariant p2;
p2.vt = VT_I4;
p2.lVal = 12;
d.Invoke1(1, &p1, NULL);
d.Invoke1(2, &p2, NULL);
d.Release();
}
::CoUninitialize();
IDispatch ATL实现方法下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219