Win32编程点滴5 - 响应ActiveX控件的事件

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

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

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;
		
		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的构造函数)。

代码下载

转载于:https://www.cnblogs.com/Greatest/archive/2009/09/09/1563173.html

jacob-1.19-x64.dll jacob-1.19-x86.dll jacob.jar LICENSE.TXT BuildingJacobFromSource.html EventCallbacks.html JacobComLifetime.html JacobThreading.html ReleaseNotes.html UsingJacob.html allclasses-frame.html allclasses-noframe.html constant-values.html deprecated-list.html help-doc.html index-all.html index.html overview-frame.html overview-summary.html overview-tree.html package-list script.js serialized-form.html stylesheet.css ComException.html ComFailException.html ComThread.html Currency.html DateUtilities.html Dispatch.html DispatchEvents.html DispatchIdentifier.html DispatchProxy.html EnumVariant.html InvocationProxy.html InvocationProxyAllVariants.html JacobException.html JacobObject.html JacobReleaseInfo.html LibraryLoader.html MainSTA.html NotImplementedException.html package-frame.html package-summary.html package-tree.html package-use.html ROT.html SafeArray.html STA.html Variant.html VariantUtilities.html VariantViaEvent.html WrongThreadException.html ComException.html ComFailException.html ComThread.html Currency.html DateUtilities.html Dispatch.html DispatchEvents.html DispatchIdentifier.html DispatchProxy.html EnumVariant.html InvocationProxy.html InvocationProxyAllVariants.html JacobException.html JacobObject.html JacobReleaseInfo.html LibraryLoader.html MainSTA.html NotImplementedException.html ROT.html SafeArray.html STA.html Variant.html VariantUtilities.html VariantViaEvent.html WrongThreadException.html ActiveXComponent.html ActiveXDispatchEvents.html ActiveXInvocationProxy.html package-frame.html package-summary.html package-tree.html package-use.html ActiveXComponent.html ActiveXDispatchEvents.html ActiveXInvocationProxy.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值