现在的位置: 首页 > 综合 > 正文

IDispEventSimpleImpl和IDispEventImpl应用分析

2013年07月30日 ⁄ 综合 ⁄ 共 2939字 ⁄ 字号 评论关闭

非常感谢原作者ysjyniiq的博客,原文博客地址如下:

http://blog.csdn.net/ysjyniiq/article/details/6428809

ATL所自带响应Event的类有两个

IDispEventSimpleImpl

IDispEventImpl

它们的区别是一个是否带类型库,现在看看他们的模板参数

template <UINT nID, class T, const IID* pdiid>
class ATL_NO_VTABLE IDispEventSimpleImpl : public _IDispEventLocator<nID, pdiid>
{
};
 
template <UINT nID, class T, const IID* pdiid = &IID_NULL, const GUID* plibid = &GUID_NULL, WORD wMajor = 0, WORDwMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispEventImpl : public IDispEventSimpleImpl<nID, T, pdiid>
{
};

可以看到IDispEventImpl的参数太多,居然连pdiid都可以默认参数。

区别:

IDispEventImpl能从IUnknow里QueryInterface出它的类型库,得到Event函数的详细结构,使下面SINK结构构造简单。而IDispEventSimpleImpl不使用类型库,使用手工打造,SINK结构自然要麻烦一些。

参数说明:

nID:控件ID,如果所有父类不是对话框类,这个值,可以随便设一个。但必须跟下面的SINK结构的ID相一致。

T:父类

pdiid:EVENT的IID。如果SINK结构是使用SINK_ENTRY_EX或SINK_ENTRY_INFO,里面的iid必须跟这里一致。

plibid:LIBID。如果pdiid用默认值IID_NULL,这个值最好用默认值。原因下面再解析。

wMajor和wMinor:版本信息。其用法跟plibid一样。

使用:

连接:

可供选择两个函数。

HRESULT DispEventAdvise(IUnknown* pUnk, const IID* piid)
HRESULT Advise(IUnknown *punk)

DispEventAdvise:

适用于是默认连接的情况(只要COM对象提供有EVENT就可以连接),这种情况由指定的piid进行连接。

但前提IDispEventImpl或IDispEventSimpleImpl必须有指定参数,不能采用默认参数,否则很可能会工作不正常。

Advise:

适用于默认连接的情况,这个函数用起来很舒服。它里面使调用AtlGetObjectSourceInterface获得默认类型信息,并存起来。仅适用于IDispEventImpl。

如果IDispEventSimpleImpl会因为在invoke时候发现有类型信息,但又不能处理类型信息而产生错误

断开:

HRESULT Unadvise(IUnknown *punk);
HRESULT DispEventUnadvise(IUnknown* pUnk, const IID* piid);

最好跟连接的相配套,但Unadvise最终会调用DispEventUnadvise,所以也可以直接DispEventUnadvise。

IDispEventImpl如果使用默认值的情况下,也就是使用Advise的情况下,其模板的构造版本,使不使用默认值,不重要,因为它会使用AtlGetObjectSourceInterface获得IID,LIBID,及版本信息。

但如果使用DispEventAdvise,IDispEventImpl的构造就相对麻烦很多,全部参数,都需要老老实实地填 ,而且不能错。IID,LIBID一般都能从头文件里得到,但版本呢?可以使用AtlGetObjectSourceInterface执行一次,查看一下,得到具体值,再构造这个模板。

SINK结构:

下面再看一下SINK结构吧。

     BEGIN_SINK_MAP(className)
         SINK_ENTRY(IDC_A171, 1, TextA171)      SINK_ENTRY_EX(2,__uuidof(HTMLDocumentEvents),DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN,func)
SINK_ENTRY_INFO(1,__uuidof(_Itt3Events) , 1, OnMyFunc, &StatusChangeInfo)
     END_SINK_MAP()

SINK结构与传统的MAP结构类似,也是begin-end结构,上面的结构是所有IDispEventSimpleImpl和IDispEventImpl共用。

从上面代码可以看到最简单是SINK_ENTRY,接着是SINK_ENTRY_EX, SINK_ENTRY_INFO,现在再来看看这三个结构的关系

#define SINK_ENTRY_INFO(id, iid, dispid, fn, info) {id, &iid, (int)(INT_PTR)(static_cast<ATL::_IDispEventLocator<id, &iid>*>((_atl_event_classtype*)8))-8, dispid, (void (__stdcall _atl_event_classtype::*)())fn, info},
#define SINK_ENTRY_EX(id, iid, dispid, fn) SINK_ENTRY_INFO(id, iid, dispid, fn, NULL)
#define SINK_ENTRY(id, dispid, fn) SINK_ENTRY_EX(id, IID_NULL, dispid, fn)

SINK_ENTRY会从SINK_ENTRY_EX逐步演变为SINK_ENTRY_INFO,复杂的代码不说,光说应用吧。

参数说明:

id:控件ID,与前面的Event类的ID相一致

dispid:EVENT里函数或属性的Dispatch ID

iid:EVENT的IID

fn:函数名字

info:函数参数结构,是最复杂的一项。(这里暂不解析,由于麻烦,一般不采用这种方式)

SINK_ENTRY:

所以在EVENT模板参数的pdiid必须为IID_NULL,否则只能使用SINK_ENTRY_EX或SINK_ENTRY_INFO,但如果在IDispEventSimpleImpl使用IID_NULL作为IDD参数,尽管可以使用SINK_ENTRY通过编译,但最终运行的时候,没有类型信息的函数,所以最后还是运行错误。简单的话说,这个宏只能用于IDispEventImpl,而且pdiid==IID_NULL的情况。

SINK_ENTRY_EX:

也只能用于IDispEventImpl,需要IID参数

SINK_ENTRY_INFO:

是最复杂的情况,可以用于IDispEventImpl和IDispEventSimpleImpl,但要要构造麻烦info参数,这不一是一件爽事。

抱歉!评论已关闭.