WTL应用程序接收处理ActiveX控件事件的实现

使用ATL编写一个带有事件的ActiveX控件后,在VB中测试事件是很方便的,在MFC Application中可以通过“从ActiveX控件导入MFC类”办法来得到控件的包装类,然后插入该控件到窗体上,就可以很方便的处理控件的事件。

使用WTL的BEGIN_SINK_MAP…END_SINK_MAP也可以较方便的处理控件的事件(在MFC中试过此种方法,目前还无法实现),而不需要生成控件类和插入控件,移植性比较好,在此写些实现的步骤和方法。(以VS2003中的VC7.1为例)

1. 生成ActiveX控件

首先创建一个ATL Project项目,如果不需要Attibuted可以把默认钩选去掉(我还没用过这个功能,暂不时其用意)。生成的“工程名+PS”项目好象没有多大意义,我一般都直接删除。

添加一个ATL Control控件(在Class View中右击项目选Add Class,选中ATL类然后在右边找到ATL Control,这跟VC6不太一样),在Options中要选中Connection points以支持事件。

添加接口属性的方法(在Class View中右击接口,弹出菜单可以打开向导),添加事件(在Class View中右击“接口+Events”接口,需要展开“项目名+Lib”才能看到),事件一般都是方法。

添加连接点(在Class View中右击生成的控件类,选择“Add Connection Point”)。

在需要发事件的地方调用“Fire_+事件方法名”来发出事件。

2. WTL中接收事件

创建一个基于对话框的WTL工程,如果没有把WTL的include添加到VC7.1的include路径中,在打资源时会提示找不到atlres.h,解决方法可以是把WTL的include目录添加到VC7.1的include路径中,也可以分别添加到工程中C/C++、Resources、MIDL的Addional Include Directories中。

把控件的IDL文件添加到工程中,编译生成“项目名.h”文件,将其包含到MainDlg.h中,同时也应包含resource.h,向导没有自己包含。

定义控件的事件相关信息:(XXX是唯一标识符)
namespace XXX
{
 enum{
  ID = 给控件指定的ID值, 
  事件枚举1 = IDL中定义的对应事件的ID值,
  事件枚举2 = IDL中定义的对应事件的ID值,
 };

 struct __declspec(uuid("IDL中定义的Events的UUID")) Events;
 struct __declspec(uuid("IDL中定义的Lib的UUID")) Library;
};

CMainDlg类增加从IDispEventImpl派生:
public IDispEventImpl<XXX::ID, CMainDlg, &__uuidof(XXX::Events), &__uuidof(XXX::Library), 1, 0>

定义事件映射和处理事件的方法并实现该方法:
 BEGIN_SINK_MAP(CMainDlg)
  SINK_ENTRY_EX(XXX::ID, __uuidof(XXX::Events), XXX::事件枚举1, 事件处理函数1)
  SINK_ENTRY_EX(XXX::ID, __uuidof(XXX::Events), XXX::事件枚举2, 事件处理函数2)
 END_SINK_MAP()
 HRESULT __stdcall 事件处理函数1([参数]);
 HRESULT __stdcall 事件处理函数2([参数]);

定义一个控件的对象并在OnInitDialog中创建它:
  CAxWindow m_wndCtrl;

  CRect rcDef(10, 10, 240, 120);
  DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  m_wndCtrl.Create(m_hWnd, rcDef, NULL, dwStyle);
  CComPtr<控件接口> pCtrl;
  HRESULT hr = m_wndCtrl.CreateControl(L"控件的ProgID");
  m_wndCtrl.QueryControl(&pCtrl);
  DispEventAdvise(pCtrl);

调用控件方法的实现:
 CComPtr<控件接口> pCtrl;
 m_wndCtrl.QueryControl(&pCtrl);
 pCtrl->控件方法([参数]);

修改_tWinMain中的“hRes = _Module.Init(NULL, hInstance);”为“hRes = _Module.Init(NULL, hInstance, &LIBID_ATLLib);”。

在stdafx.h中包含WTL的相关头文件,比如:
#include <atlframe.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <atlctrlw.h>
#include <atlsplit.h>
#include <atlmisc.h>
#include <atlcoll.h>
#include <atlddx.h>

如果需要使用GetDlgItemText和CString等,需要声明_WTL_NO_CSTRING宏禁掉WTL的CString类,并在stdafx.h中atlapp.h之前包含atlstr.h:
#include <atlstr.h>
#include <atlapp.h>

编译Release版本时需要忽略atlmincrt.lib,否则会提示以下错误:
Linking…
LIBCMT.lib(crt0dat.obj) : error LNK2005: __osplatform already defined in atlmincrt.lib(atlinit.obj)
LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function _mainCRTStartup

Tags:

Leave a Reply