首先我们来回顾下ConnectionPo概念任何支持事件对象(比如ActiveXControl控件)都支持IConnectionPoContainer接口顾名思义就是个IConnectionPo容器包含了这个对象支持全部事件IConnectionPo代表了组事件IConnectionPo::Advise并传入我们想要接收事件对象指针而IConnectionPo::GetConnectionInterface返回IID是此接收事件对象必须实现接口否则Advise会失败来看下AxAdviseAll代码:
//枚举IConnectionPoContainer中每个IConnectionPo
//对于每个IConnectionPo新建个支持iid接口CGeneralEventSink对象并Advise
//pUnk是Control控件指针
HRESULT AxAdviseAll(IUnknown * pUnk)
{
HRESULT hr;
IConnectionPoContainer * pContainer = NULL;
IConnectionPo * pConnectionPo=NULL;
IEnumConnectionPos * pEnum = NULL;
hr = pUnk->QueryInterface(IID_IConnectionPoContainer,(void**)&pContainer);
(FAILED(hr)) goto error1;
hr = pContainer->EnumConnectionPos(&pEnum);
(FAILED(hr)) goto error1;
ULONG uFetched;
while(S_OK (pEnum->Next(1,&pConnectionPo,&uFetched)) && uFetched>=1)
{
DWORD dwCookie;
IID iid;
hr = pConnectionPo->GetConnectionInterface(&iid);
(FAILED(hr)) iid = IID_NULL;
//这里传入pUnk是为了通过pUnk得到iidITypeInfo进而判断iid是否是Dispatch接口
IUnknown * pSink = CGeneralEventSink(iid,pUnk);
hr = pConnectionPo->Advise(pSink,&dwCookie);
pSink->Release;
pConnectionPo->Release;
pConnectionPo = NULL;
pSink = NULL;
}
hr = S_OK;
error1:
(pEnum)pEnum->Release;
(pContainer) pContainer->Release;
(pConnectionPo) pConnectionPo->Release;
hr;
}
然后来说下Dispath事件如果IConnectionPo::GetConnectionInterface返回IID代表接口是继承于IDispatch话这个事件就是个Dispath事件当事件发生时产生事件对象不会直接pObj->OnEvent而会pObj->Invoke(…)例如FlashControl控件事件对象:(通过vc#import指令生成)
struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8-444553540000"))
_IShockwaveFlashEvents : IDispatch
{
//
// Wrapper methods for error-handling
//
// Methods:
HRESULT OnReadyStateChange (
long State );
HRESULT OnProgress (
long percentDone );
HRESULT FSCommand (
_bstr_t command,
_bstr_t args );
HRESULT FlashCall (
_bstr_t request );
};
当FlashControl控件产生事件时它会IDispath::Invoke而不是OnReadyStateChange等思路方法只有在vcatl等框架里面这些框架会自动生成Invoke在Invoke中根据参数区别(第个参数memid代表代表哪个思路方法被)再OnReadyStateChange等思路方法
所以虽然IConnectionPo要求实现接口是_IShockwaveFlashEvents但是我们虚表中只要存在IDispath思路方法即可虚表中的后_IShockwaveFlashEvents思路方法不会被直接(如果我们自己实现Invoke也不会去)我们告诉FlashControl控件这是个_IShockwaveFlashEvents接口虽然它只实现了IDispath就像CGeneralEventSink中代码:
STDMETHOD(QueryInterface(REFIID riid,void **ppvObject))
{
*ppvObject = NULL;
( IID_IUnknown riid)
{
*ppvObject = (IUnknown*)this;
}
(IID_IDispatch riid || m_iid riid)
{
*ppvObject = (IDispatch*)this;
}
{
E_NOINTERFACE;
}
AddRef;
S_OK;
}
其中m_iid是IConnectionPo要求实现接口通过CGeneralEventSink构造参数传入当然m_iid定要是个dispatch接口(继承于IDispatch)如果IConnectionPo要求接口不是继承于IDispatch则m_iid会被设置为IID_NULL然后IConnectionPo便得不到所要求接口Advise就会失败否则话Control控件就会个虚表中不存在思路方法(不是IDispatch事件话Control控件只能直接接口思路方法而不是Invoke)产生
那么如何判断m_iid是Dispatch接口呢?首先得到代表此接口ITypeInfo指针这个请参考代码中:
HRESULT CGeneralEventSink::GetIIDTypeInfo(IID iid,ITypeInfo ** ppInfo,IUnknown * pRelateObj);
简单说,就是Control控件接口pRelateObj会实现IProvideClassInfo接口来提供它本身ITypeInfo再通过ITypeInfo::GetRefTypeInfo可以得到相关事件ITypeInfo
接着此ITypeInfoTYPEATTR结构中typekind表明了此ITypeInfo是否Dispatch接口代码如下:
TYPEATTR *attr;
(SUCCEEDED(pInfo->GetTypeAttr(&attr)))
{
(attr->typekind TKIND_DISPATCH) isDispatch = true;
pInfo->ReleaseTypeAttr(attr);
}
最后CGeneralEventSinkIDispatch思路方法全部返回E_NOTIMPLE就可以了毕竟Control控件只是通知我们事件发生了而不关心我们有什么反应当然为了让提供举例更有趣代码里面Invoke做了详细log(在得到接口ITypeInfo同时也枚举了MEMID/DISPID对应名字还从注册表中把iid变成了接口名字所有这切参考CGeneralEventSink构造)
本文举例源代码或素材下载
最新评论