您的位置:首页 > 其它

8.MFC实现IDispatch自动化接口

2016-07-30 19:58 477 查看
前文叙述了IDispatch接口的原理,本文先讲MFC的实现细节,下文讲ATL的实现细节。

1.通用方法

MFC不使用类型库,这里先讲不用类型库实现IDispatch,此时一般实现GetIDsOfNames和Invoke函数。这里使用MFC实现,实际上在ATL中也可以使用。

按照之前讲的通用接口的编写方法,定义嵌入类和工厂类声明如下,嵌入类实现了IDispatch接口。

//接口映射表
BEGIN_INTERFACE_PART(Cat, IDispatch)
INIT_INTERFACE_PART(CAnimalObject, Cat)

virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo);

virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo);

virtual HRESULT STDMETHODCALLTYPE 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);

virtual /* [local] */ HRESULT STDMETHODCALLTYPE 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);
END_INTERFACE_PART_STATIC(Cat)

DECLARE_INTERFACE_MAP()

建立接口映射表如下

//接口映射表
BEGIN_INTERFACE_MAP(CAnimalObject, CCmdTarget)
INTERFACE_PART(CAnimalObject, IID_IDispatch, Cat)
END_INTERFACE_MAP()


为了不依赖类型库实现IDispatch接口,建立名字和DispID如下

//建立Dispatch表
map<CString, UINT> g_DispMap;

CAnimalObject::CAnimalObject(void)
{
g_DispMap[L"SayHello1"] = DISP_ID_SAYHELLO1;
g_DispMap[L"SayHello2"] = DISP_ID_SAYHELLO2;
}


由于没有类型库,则GetTypeInfoCount和GetTypeInfo不用实现,具体如下:

HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo)
{
*pctinfo = 0;	//没有类型库
return S_OK;
}

HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
{
*ppTInfo = NULL;
return E_NOTIMPL;
}


GetIDsOfNames的实现只需要查表即可,如下:

HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::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)
{

for (UINT i=0; i<cNames; i++)
{
map<CString, UINT>::iterator iter = g_DispMap.find(rgszNames[i]);
if ( g_DispMap.end() != iter )
{
rgDispId[i] = iter->second;
}
else
{
rgDispId[i] = DISPID_UNKNOWN;
}
}

return S_OK;
}

这里可能存在一次性传入多个Name的情况,此时cNames标示传入的name个数,rgszNames和rgDispID均为数组。

Invoke根据传入的分发ID,调用不同的逻辑:

HRESULT STDMETHODCALLTYPE CAnimalObject::XCat::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)
{
if (0==dispIdMember ||
(dispIdMember!=DISP_ID_SAYHELLO1 && dispIdMember!=DISP_ID_SAYHELLO2) ||
0==(DISPATCH_METHOD&wFlags))
{
return E_NOTIMPL;
}

if (pVarResult)
{
CComVariant var(true);
*pVarResult = var;
}

USES_CONVERSION;

switch (dispIdMember)
{
case DISP_ID_SAYHELLO1:
if (pDispParams &&							//参数数组有效
pDispParams->cArgs==1 &&				//参数个数为1
pDispParams->rgvarg[0].vt==VT_BSTR &&	//参数类型满足
pDispParams->rgvarg[0].bstrVal)			//参数值有效
{
CString strVal(OLE2T(pDispParams->rgvarg[0].bstrVal));
wcout << L"猫猫说我的名字叫:" << strVal.GetBuffer(0) << endl;
}
break;

case DISP_ID_SAYHELLO2:
if (pDispParams &&							//参数数组有效
pDispParams->cArgs==1 &&				//参数个数为1
pDispParams->rgvarg[0].vt==VT_I4 &&		//参数类型满足
pDispParams->rgvarg[0].intVal)			//参数值有效
{
wcout << L"猫猫说我的年龄是:" << pDispParams->rgvarg[0].intVal << endl;
}
break;
}

return S_OK;
}


2.标准MFC的实现方法

MFC中我们已经见到了各种查表,如消息映射表MESSAGE_MAP,接口映射表INTERFACE|_MAP等。同样为了支持IDISPATC接口,MFC做了一套分发映射表DISPATCH_MAP,和之前的使用方法一样。

另外,MFC中的CCmdTarget默认实现了IDispatch接口,只要我们在子类构造函数调用EnableAutomation开启自动化支持即可。此时不用再单独添加接口映射表,MFC已默认将IDispatch接口加到接口查询表中。

MFC这套机制非常简单,如下:

声明分发映射表:

//分派映射表
DECLARE_DISPATCH_MAP()
实现分发映射表:

//分配映射表
BEGIN_DISPATCH_MAP(CAnimalObject, CCmdTarget)
DISP_FUNCTION_ID(CAnimalObject, "SayHello1", DISP_ID_SAYHELLO1, SayHello1, VT_I4, VTS_BSTR)// "SayHello1"不要加L前缀
DISP_FUNCTION_ID(CAnimalObject, "SayHello2", DISP_ID_SAYHELLO2, SayHello2, VT_I4, VTS_I4)
END_DISPATCH_MAP()


DISP_FUNCTION_ID宏参数分别为当前类名,函数名,分发ID,函数指针,函数返回值,函数参数

对应的调用函数逻辑实现如下:
BOOL CAnimalObject::SayHello1( BSTR szWord )
{
USES_CONVERSION;
CString strWord(OLE2CW(szWord));

wcout << L"猫猫2的名字:" << strWord.GetBuffer(0) << endl;
return TRUE;
}

BOOL CAnimalObject::SayHello2( int nAge )
{
wcout << L"猫猫2的年龄:" << nAge << endl;
return TRUE;
}


3.调用IDispatch接口

默认的IDispatch接口调用Invoke函数时参数太繁琐,MFC提供COleDispatchDriver类来辅助操作,如下:

//初始化COM库
if (CoInitialize(NULL) != S_OK)
{
wcout << L"Fail to Initialize COM" << endl;
return -1;
}

//自动化调用
COleDispatchDriver d;
if (d.CreateDispatch(CLSID_AnimalObject))
{
BYTE params1[] = {VTS_BSTR};
BYTE params2[] = {VTS_I4};
BOOL bRet;

d.InvokeHelper(DISP_ID_SAYHELLO1, DISPATCH_METHOD, VT_I4, (LPVOID)&bRet, params1, L"maomao");
d.InvokeHelper(DISP_ID_SAYHELLO2, DISPATCH_METHOD, VT_I4, (LPVOID)&bRet, params2, 20);

d.ReleaseDispatch();
}

::CoUninitialize();

InvokeHelper参数依次为分发ID,方法Flag,返回类型,返回值,函数参数类型数组,函数参数。

IDispatch通用实现方法下载链接

IDispatch MFC实现方法下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: