之前写的“WTL应用程序接收处理ActiveX控件事件的实现”文章已经描述了如何处理ActiveX的事件,但那个方法实施起来比较繁锁,也不易读解。这里介绍更简便的方法,甚少不需要一个一个的将UUID拷到WTL工程来用,而是直到使用IDL文件编译出来的结果,这样的好处是当这些UUID更改后重新编译就可以了,不需要修改源码。
首先我们写的ActiveX控件可以将IDL公开给使用者(仅项目内或公司内),如果直接把IDL文件给使用者,后者使用#include方法把该IDL包含到其IDL中,编译时可能会有问题,毕竟ActiveX控件的library对使用者是没有意义的反而会导致错误。
解决办法是:(假设工程名是Test,接口是TestCtrl)
1. 在完成接口和事件定义后将library TestLib部分移出到TestLib.idl文件中,原来的idl文件删除该library项只保留interface ITestCtrl和dispinterface _ITestCtrlEvents,在TestLib.idl中使用#include "Test.idl"包含接口。
2. 然后将TestLib.idl添加到工程并删除原来的IDL文件,将Test.idl移到公用目录下(别人可以直接使用),并将该公用目录添加到MIDL的Addional Inlcude Directories中。
3. 将interface和dispinterface中的所有id值定义成宏,文件名格式可以是工程名+DISPIDs.h(与Test.idl放在同一目录下),比如TestDISPIDs.h,在该宏定义文件加上事件的扩展声明,以后用到__uuidof(_ITestCtrlEvents)的地方全用DIID_ITestCtrlEvents代替:
#ifdef __cplusplus
extern "C" const IID DIID__ITestCtrlEvents;
#else
extern const IID DIID__ITestCtrlEvents;
#endif
4. 生成连接点后在_ITestCtrlEvents_CP.h包含TestDISPIDs.h文件。添加实现代码,编译生成DLL并注册,下一步使用。
在WTL工程中,使用IDispEventSimpleImpl代替IDispEventImpl,前者不再需要Lib的UUID,处理方法是:
1. 在MainDlg.h中包含TestDISPIDs.h,定义一个控件ID比如“#define CONTROL_ID 1001”,修改CMainDlg类从IDispEventSimpleImpl中重载:
public IDispEventSimpleImpl<CONTROL_ID, CMainDlg, &DIID__ITestCtrlEvents>
2. 在CMainDlg中添加SINK_MAP:
BEGIN_SINK_MAP(CMainDlg)
SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_CLICK, OnItemClick, &s_infoOnItemClick)
SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_DBLCLK, OnItemDblClk, &s_infoOnItemDblClk)
SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_CHANGED, OnItemChanged, &s_infoOnItemChanged)
END_SINK_MAP()
3. 添加事件处理函数:
STDMETHOD(OnItemClick)(IBaseNode* pItem);
STDMETHOD(OnItemDblClk)(IBaseNode* pItem);
STDMETHOD(OnItemChanged)(IBaseNode* pNewItem, IBaseNode* pOldItem);
4. 定义Entry Info为static:
static _ATL_FUNC_INFO s_infoOnItemClick;
static _ATL_FUNC_INFO s_infoOnItemDblClk;
static _ATL_FUNC_INFO s_infoOnItemChanged;
5. 在cpp文件中定义Entry Info实体:
_ATL_FUNC_INFO CMainDlg::s_infoOnItemClick =
{
CC_STDCALL, // Calling convention.
VT_I4, // Return type.
1, // Number of arguments.
{VT_DISPATCH} // Argument types.
};
_ATL_FUNC_INFO CMainDlg::s_infoOnItemDblClk =
{
CC_STDCALL, // Calling convention.
VT_I4, // Return type.
1, // Number of arguments.
{VT_DISPATCH} // Argument types.
};
_ATL_FUNC_INFO CMainDlg::s_infoOnItemChanged =
{
CC_STDCALL, // Calling convention.
VT_I4, // Return type.
2, // Number of arguments.
{VT_DISPATCH, VT_DISPATCH} // Argument types.
};
6. 定义事件函数实体:
STDMETHODIMP CMainDlg::OnItemClick(IBaseNode* pItem){…}
STDMETHODIMP CMainDlg::OnItemDblClk(IBaseNode* pItem){…}
STDMETHODIMP CMainDlg::OnItemChanged(IBaseNode* pNewItem, IBaseNode* pOldItem){…}
7. 将Test.idl包含到WTL工程的IDL文件中(没有则创建一个与工程同名的IDL),编译IDL后将xxx_i.c添加到工程中,编译整个WTL工程,测试。