16.MFC实现IDispatch自动化接口

前文叙述了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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值