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

首先,我们来回顾一下ConnectionPoint的概念。任何支持事件的对象(比如,ActiveX控件),都支持IConnectionPointContainer接口,顾名思义就是一个IConnectionPoint的容器,包含了这个对象支持的全部事件。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的构造函数)。

代码下载

Win32编程点滴5 - 响应ActiveX控件的事件的更多相关文章

  1. Win32编程点滴3 - 简单ActiveX控件的使用

    虽然这里一片的.net气氛,到处充斥着像MVC.WPF.WorkFlow.LINQ等各种niubility的术语.但我们使用的Windows还是由COM技术主宰着:我们在选择日常使用的软件时,也会避免 ...

  2. 如何给ActiveX控件添加“事件”“属性”“标准事件”“自定义事件”等一些相关操作

    上一篇小编带大家熟悉了一下ActiveX的建立以及相关的概念,(http://blog.csdn.net/u014028070/article/details/38424611) 本文介绍下如何给控件 ...

  3. c# 开发ActiveX控件,添加事件,QT调用事件

    c# 开发 ActiveX 的过程参考我的另一篇文章 :  https://www.cnblogs.com/baqifanye/p/10414004.html 本篇讲如何 在C# 开发的ActiveX ...

  4. 用ATL和MFC来创建ActiveX控件

    摘要:目前MFC和ATL代表了两种框架,分别面向不同类型的基于Windows的开发.MFC代表了创建独立的Windows应用的一种简单.一致的方法:ATL提供了一种框架来实现创建COM客户机和服务器所 ...

  5. MFC-[转]基于MFC的ActiveX控件开发

    作者:lidan | 出处:博客园 | 2012/3/13 16:10:34 | 阅读22次 ActiveX 控件是基于组件对象模型 (COM) 的可重用软件组件,广泛应用于桌面及Web应用中.在VC ...

  6. 【转载】基于MFC的ActiveX控件开发(3)

    原文:http://iysm.net/?p=122 3.事件 ActiveX 控件使用事件通知容器控件上发生了某些事情.事件的常见示例包括单击控件.使用键盘输入数据和控件状态更改.当发生这些操作时,控 ...

  7. 以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件

    以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件 2007/10/29 Mark BukovecEmpire Down Development 适用于:Mi ...

  8. Qt 环境下的activex控件编程-------1

    本人第一次接触这种activeX控件的东西,参考了网上很多的教程,终于耗时三个多小时初步理解并编写了一个小demo,现在分享给大家,希望大家少走弯路.步骤如下: 1>像平常创建项目一样创建一个d ...

  9. 让动态创建的ActiveX控件响应Windows消息

    当我们通过 CWnd::CreateControl() 动态创建 ActiveX   控件时, Windows 消息并不会被发送给我 们的由   CWnd 派生得控件类.例如,即使我们为 WM_KIL ...

随机推荐

  1. 网页Tab控件

    网页Tab控件 找到:http://www.open-open.com/ajax/2_Tabs.htm 页面,查看了若干Tab控件, 找到了:http://www.open-open.com/ajax ...

  2. Testing Is the Engineering Rigor of Software Development

    Testing Is the Engineering Rigor of Software Development Neal Ford DEVELOPERS LOVE TO USE TORTURED M ...

  3. APP公布到应用市场(苹果APP STORE+安卓各大应用市场)

    注意事项 1.应用要签名,为了以后可以顺利更新应用.要保持每次的签名一致,所以要妥善保管好签名数据. 2.进行公布測试,最好有个检查表,每次公布的时候进行核查. 苹果APP STORE 一.证书的导出 ...

  4. Ffmpeg 获取USB Camera 视频流

    本文讲述的案例是如何通过Ffmpeg实现从USB Camera中获取视频流并将视频流保存到MP4文件. 本文亦适用于从USB Camera 获取视频流并将视频流转发到rtmp服务的案例,二者基本的原理 ...

  5. Java EJB JBoss

    JBoss:JBoss是web服务器的一种,主要做EJB容器,和tomcat集成就可以jsp,servlet,ejb通吃了JBoss有两种版本,一种是独立的,一种是和tomcat集成的,当然都是免费的 ...

  6. ppycharm设置解释器版本号码

    在pycharm中点击File,之后在弹出的窗口中输入Project Interpreter,点击之后就可以看到自己的解释器版本是多少了,也可以随意选择想要用的解释器版本号码:

  7. UVA1422-Processor(二分法+优先队列)

    option=com_onlinejudge&Itemid=8&category=512&page=show_problem&problem=4168"> ...

  8. codevs1032

    题目地址:http://codevs.cn/problem/1032/ 分析: 题目数据有错.这题过不了才正常. 我调了非常久可是就是有两个点过不去.于是我把数据下了下来,找到WA的第五个点和第七个点 ...

  9. PostgreSQL与MySQL比較

    特性 MySQL PostgreSQL 实例 通过执行 MySQL 命令(mysqld)启动实例. 一个实例能够管理一个或多个数据库.一台server能够执行多个 mysqld 实例.一个实例管理器能 ...

  10. Create an OData v4 Endpoint Using ASP.NET Web API 2.2(使用ASP.NET Web API 2.2创建OData v4端点)

    开放数据协议Open Data Protocol(OData)是web的一种数据存取协议,OData通过设置CRUD操作(Create创建.Read读取.Update更新,Delete删除)提供一种统 ...