博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Win32编程点滴5 - 响应ActiveX控件的事件
阅读量:6989 次
发布时间:2019-06-27

本文共 3760 字,大约阅读时间需要 12 分钟。

在最近的中说到了,如何创建ActiveX,这次我们来响应事件。这次,我们将创建一个类:CGeneralEventSink,它能够响应任何Dispatch事件(事件的接口继承与IDispatch)。

首先,我们来回顾一下ConnectionPoint的概念。任何支持事件的对象(比如,ActiveX控件),都支持接口,顾名思义就是一个的容器,包含了这个对象支持的全部事件。IConnectionPoint代表了一组事件,调用IConnectionPoint::Advise并传入我们想要接收事件的对象指针。而IConnectionPoint::GetConnectionInterface返回的IID,是此接收事件的对象必须实现的接口,否则Advise会失败。来看一下AxAdviseAll的代码:

//枚举IConnectionPointContainer中的每个IConnectionPoint, //对于每个IConnectionPoint新建一个支持iid接口的CGeneralEventSink对象,并Advise。 //pUnk是控件的指针HRESULT AxAdviseAll(IUnknown * pUnk){	HRESULT hr;	IConnectionPointContainer * pContainer = NULL;	IConnectionPoint * pConnectionPoint=NULL;	IEnumConnectionPoints * pEnum = NULL;	hr = pUnk->QueryInterface(IID_IConnectionPointContainer,(void**)&pContainer);	if (FAILED(hr)) goto error1;	hr = pContainer->EnumConnectionPoints(&pEnum);	if (FAILED(hr)) goto error1;	ULONG uFetched;	while(S_OK == (pEnum->Next(1,&pConnectionPoint,&uFetched)) && uFetched>=1)	{		DWORD dwCookie;		IID iid;		hr = pConnectionPoint->GetConnectionInterface(&iid);		if (FAILED(hr)) iid = IID_NULL;		//这里传入pUnk是为了通过pUnk得到iid的ITypeInfo,进而判断iid是否是Dispatch接口		IUnknown * pSink = new CGeneralEventSink(iid,pUnk);		hr = pConnectionPoint->Advise(pSink,&dwCookie);		pSink->Release();		pConnectionPoint->Release();		pConnectionPoint = NULL;		pSink = NULL;	}	hr = S_OK;error1:	if (pEnum)pEnum->Release();	if (pContainer) pContainer->Release();	if (pConnectionPoint) pConnectionPoint->Release();	return hr;}

 

然后,来说一下Dispath事件。如果IConnectionPoint::GetConnectionInterface返回的IID代表的接口是继承于IDispatch的话,这个事件就是一个Dispath事件。当事件发生时,产生事件的对象不会直接调用pObj->OnEvent(),而会调用pObj->Invoke(…)。例如,Flash控件的事件对象:(通过vc的#import指令生成的)

struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000")) _IShockwaveFlashEvents : IDispatch {
// // Wrapper methods for error-handling // // Methods: HRESULT OnReadyStateChange ( long newState ); HRESULT OnProgress ( long percentDone ); HRESULT FSCommand ( _bstr_t command, _bstr_t args ); HRESULT FlashCall ( _bstr_t request ); };

当Flash控件产生事件时,它会调用IDispath::Invoke而不是,OnReadyStateChange等方法。只有在vc,atl等框架里面,这些框架会自动生成Invoke函数,在Invoke函数中根据参数的不同(第一个参数memid代表代表哪个方法被调用),再调用OnReadyStateChange等方法。

所以,虽然IConnectionPoint要求实现的接口是_IShockwaveFlashEvents,但是我们的虚表中,只要存在IDispath的方法即可,虚表中之后_IShockwaveFlashEvents的方法不会被直接调用(如果我们自己实现Invoke,也不会去调用的)。我们告诉Flash控件,这是一个_IShockwaveFlashEvents接口,虽然它只实现了IDispath。就像CGeneralEventSink中的代码:

STDMETHOD(QueryInterface(REFIID riid,void **ppvObject)) {
*ppvObject = NULL; if ( IID_IUnknown == riid) {
*ppvObject = (IUnknown*)this; } else if (IID_IDispatch == riid || m_iid == riid) {
*ppvObject = (IDispatch*)this; } else {
return E_NOINTERFACE; } AddRef(); return S_OK; }

其中m_iid是IConnectionPoint要求实现的接口,通过CGeneralEventSink构造函数参数传入的。当然,m_iid一定要是一个dispatch接口(继承于IDispatch)。如果IConnectionPoint要求的接口不是继承于IDispatch的,则m_iid会被设置为IID_NULL,然后IConnectionPoint便得不到所要求的接口,Advise就会失败,否则的话控件就会调用一个虚表中不存在的方法(不是IDispatch事件的话,控件只能直接调用接口的方法,而不是Invoke),产生错误。

那么,如何判断m_iid是Dispatch接口呢?首先,得到代表此接口的ITypeInfo指针,这个请参考代码中的:

HRESULT CGeneralEventSink::GetIIDTypeInfo(IID iid,ITypeInfo ** ppInfo,IUnknown * pRelateObj);

简单的说,就是控件的接口pRelateObj会实现IProvideClassInfo接口,来提供它本身的ITypeInfo。再通过ITypeInfo::GetRefTypeInfo可以得到相关事件的ITypeInfo。

接着,此ITypeInfo的TYPEATTR结构中的typekind表明了,此ITypeInfo是否的Dispatch接口,代码如下:

TYPEATTR *attr; if (SUCCEEDED(pInfo->GetTypeAttr(&attr))) {
if (attr->typekind == TKIND_DISPATCH) isDispatch = true; pInfo->ReleaseTypeAttr(attr); }

最后,CGeneralEventSink的IDispatch方法全部返回E_NOTIMPLE就可以了,毕竟控件只是通知我们事件发生了,而不关心我们有什么反应。当然,为了让提供的示例更有趣,代码里面的Invoke做了详细的log(在得到接口的ITypeInfo的同时,也枚举了MEMID/DISPID对应的名字。还从注册表中把iid变成了接口的名字,所有这一切参考CGeneralEventSink的构造函数)。

转载地址:http://xszvl.baihongyu.com/

你可能感兴趣的文章
3.2 mysql登陆
查看>>
(window,parent,opener,top).location.reload方法汇总
查看>>
Spring Boot+MyBatis 多数据源配置
查看>>
极客标签:可能是目前最好的前端代码学习工具
查看>>
吐槽swoole
查看>>
springboot的三种启动方式
查看>>
【Android入门】-【AndroidManifest.xml】-【中英对照】
查看>>
error:Error running 'env GEM_PATH=/Users/*
查看>>
[更新问题]无法在安装新的版本前,为“./boot/vmlinuz-2.6.24-19-generic”做一个符号链接备份...
查看>>
UITextView 限制字符长度
查看>>
Mongodb入门系列(2)——在linux下安装、启动、关闭MongoDB以及注意事项(详细)...
查看>>
Java性能调优
查看>>
rz -b 选项传输 excel 文件
查看>>
简单的php基于curl的反向代理程序
查看>>
PDF转HTML神器pdf2htmlEX,解决跨平台问题
查看>>
jQuery易混知识点
查看>>
mysql启动故障排查
查看>>
Runtime.getRuntime.exec(String)与exec(String[])的区别
查看>>
RAID技术优势和磁盘阵列级别。
查看>>
MySQL借助ibd文件恢复数据技巧?
查看>>