原文:http://blog.sina.com.cn/s/blog_86fe5b440101a2wv.html
ATL所自带响应Event的类有两个
IDispEventSimpleImpl IDispEventImpl 它们的区别是一个是否带类型库,现在看看他们的模板参数 template class ATL_NO_VTABLE IDispEventSimpleImpl : public _IDispEventLocator { }; template const GUID* plibid = &GUID_NULL, WORD wMajor = 0, WORD wMinor = 0, class tihclass = CComTypeInfoHolder> class ATL_NO_VTABLE IDispEventImpl : public IDispEventSimpleImpl { }; 可以看到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: 仅适用于默认连接的情况,这个函数用起来很舒服。它里面使调用AtlGetObjectSourceInterf ace获得默认类型信息,并存起来。仅适用于IDispEventImpl。 如果IDispEventSimpleImpl会因为在invoke时候发现有类型信息,但又不能处理类型信息而产生错误 断开: HRESULT Unadvise(IUnknown *punk); HRESULT DispEventUnadvise(IUnknown* pUnk, const IID* piid); 最好跟连接的相配套,但Unadvise最终会调用DispEventUnadvise,所以也可以直接DispEventUnadvise。 IDispEventImpl如果使用默认值的情况下,也就是使用Advise的情况下,其模板的构造版本,使不使用默认值,不重要,因为它会使用AtlGetObjectSourceInterf ace获得IID,LIBID,及版本信息。 但如果使用DispEventAdvise,IDispEventImpl的构造就相对麻烦很多,全部参数,都需要老老实实地填 ,而且不能错。IID,LIBID一般都能从头文件里得到,但版本呢?可以使用AtlGetObjectSourceInterf ace执行一次,查看一下,得到具体值,再构造这个模板。 二、SINK结构: 下面再看一下SINK结构吧。 [cpp] view plaincopy 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,现在再来看看这三个结构的关系 [cpp] view plaincopy #define SINK_ENTRY_INFO(id, iid, dispid, fn, info) {id, &iid, (int)(INT_PTR)(static_cast*>((_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参数,这不一是一件爽事。