前小节简单介绍了WTL安装注意的一点小的细节并举例简述如何对自己的窗口类添加对消息的响应,这一节主要就WTL如何实现对windows窗口过程函数的封装陈述些自己的理解。
一、ATL对Windows窗口的封装
WTL的基础是ATL。WTL的框架窗口是ATL窗口类的继承。因此,先介绍一下ATL对Windows窗口的封装。
由win32 API 中创建Windows应用程序可以知道创建窗口和窗口工作的逻辑是:
1 定义一个窗口类
2 注册窗口类
3 创建该类窗口并显示和激活该窗口
4 窗口的消息处理逻辑在窗口函数中。(该函数在注册窗口类时指定)
可以得知,对于窗口的封装关键在于怎样封装窗口消息处理机制。怎样将消息传递给相应的类的实例。通常的办法是采用虚函数:将每个消息对应生成一个虚函数,这样,在窗口处理函数中,对于每个消息,都调用其对应的虚函数即可。但带来的问题是类的虚函数表过于庞大。ATL的处理机制是只定义了一个虚函数(ProcessWindowMessage())。
图示ATL封装的类的继承关系图。从图中可以看到有两个最基本的类。一个是CWindow,另一个是CMessageMap。
在ATL类中对窗口过程的实现是CWindowImpl。它派生自CWindow、CMessageMap,CWindows是对Windows的窗口API的一个封装。它把一个Windows句柄封装了起来,并提供了对该句柄所代表的窗口的操作的API的封装。
对CMessageMap,ATL的定义是: 1class ATL_NO_VTABLE CMessageMap
2{
3public:
4 virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
5 LRESULT& lResult, DWORD dwMsgMapID) = 0;
6}; CMessageMap仅仅定义了一个抽象虚函数——ProcessWindowMessage()。在这个函数中存在许多的case语句分别对每个消息作响应处理,因此,所有的包含消息处理机制的窗口都必须实现该函数。
好,到此处我们知道WTL对于窗口消息的处理是通过子窗口类中重写基类的虚函数ProcessWindowMessage,但在前一节的例子中可发现在派生类中并没有"实际"重写这个函数,ATL通过使用宏,直接形成窗口函数的代码。 通过定义消息映射和实现消息映射中的消息处理句柄,编译器在编译时,会为你生成你的窗口类的ProcessWindowMessage()。
消息映射宏包含消息映射控制宏和消息处理宏。
1 消息映射控制宏:BEGIN_MSG_MAP()和END_MSG_MAP()
每个消息映射都由BEGIN_MSG_MAP()宏开始,并由END_MSG_MAP()结束。
#define BEGIN_MSG_MAP(theClass) Code 显然,这是函数ProcessWindowMessage()的实现。
2 消息处理宏
消息处理宏的目的是将消息和相应的处理函数(该窗口的成员函数)联系起来。
根据窗口消息的类别:普通窗口消息(如WM_CREATE),命令消息(WM_COMMANS)和通知消息(WM_NOTIFY)。
响应的消息处理宏也分类为三种:
普通消息处理宏——MESSAGE_HANDLER和MESSAGE_RANGE_HANDLER
命令消息处理宏有五个——COMMAND_HANDLER,COMMAND_ID_HANDLER,COMMAND_CODE_HANDLER,COMMAND_RANGE_HANDLER和COMMAND_RANGE_CODE_HANDLER。
通知消息处理宏有五个——NOTIFY_HANDLER,NOTIFY_ID_HANDLER,NOTIFY_CODE_HANDLER,NOTIFY_RANGE_HANDLER和NOTIFY_RANGE_CODE_HANDLER
简要看下MESSAGE_HANDLER和MESSAGE_RANGE_HANDLER两个宏的定义:
MESSAGE_HANDLER(msg, func)
可见宏中只是要将一个消息(或一定范围内的消息)和一个消息处理函数连在一起。一、ATL对Windows窗口的封装
WTL的基础是ATL。WTL的框架窗口是ATL窗口类的继承。因此,先介绍一下ATL对Windows窗口的封装。
由win32 API 中创建Windows应用程序可以知道创建窗口和窗口工作的逻辑是:
1 定义一个窗口类
2 注册窗口类
3 创建该类窗口并显示和激活该窗口
4 窗口的消息处理逻辑在窗口函数中。(该函数在注册窗口类时指定)
可以得知,对于窗口的封装关键在于怎样封装窗口消息处理机制。怎样将消息传递给相应的类的实例。通常的办法是采用虚函数:将每个消息对应生成一个虚函数,这样,在窗口处理函数中,对于每个消息,都调用其对应的虚函数即可。但带来的问题是类的虚函数表过于庞大。ATL的处理机制是只定义了一个虚函数(ProcessWindowMessage())。
图示ATL封装的类的继承关系图。从图中可以看到有两个最基本的类。一个是CWindow,另一个是CMessageMap。
在ATL类中对窗口过程的实现是CWindowImpl。它派生自CWindow、CMessageMap,CWindows是对Windows的窗口API的一个封装。它把一个Windows句柄封装了起来,并提供了对该句柄所代表的窗口的操作的API的封装。
对CMessageMap,ATL的定义是: 1class ATL_NO_VTABLE CMessageMap
2{
3public:
4 virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
5 LRESULT& lResult, DWORD dwMsgMapID) = 0;
6}; CMessageMap仅仅定义了一个抽象虚函数——ProcessWindowMessage()。在这个函数中存在许多的case语句分别对每个消息作响应处理,因此,所有的包含消息处理机制的窗口都必须实现该函数。
好,到此处我们知道WTL对于窗口消息的处理是通过子窗口类中重写基类的虚函数ProcessWindowMessage,但在前一节的例子中可发现在派生类中并没有"实际"重写这个函数,ATL通过使用宏,直接形成窗口函数的代码。 通过定义消息映射和实现消息映射中的消息处理句柄,编译器在编译时,会为你生成你的窗口类的ProcessWindowMessage()。
消息映射宏包含消息映射控制宏和消息处理宏。
1 消息映射控制宏:BEGIN_MSG_MAP()和END_MSG_MAP()
每个消息映射都由BEGIN_MSG_MAP()宏开始,并由END_MSG_MAP()结束。
#define BEGIN_MSG_MAP(theClass) Code 显然,这是函数ProcessWindowMessage()的实现。
2 消息处理宏
消息处理宏的目的是将消息和相应的处理函数(该窗口的成员函数)联系起来。
根据窗口消息的类别:普通窗口消息(如WM_CREATE),命令消息(WM_COMMANS)和通知消息(WM_NOTIFY)。
响应的消息处理宏也分类为三种:
普通消息处理宏——MESSAGE_HANDLER和MESSAGE_RANGE_HANDLER
命令消息处理宏有五个——COMMAND_HANDLER,COMMAND_ID_HANDLER,COMMAND_CODE_HANDLER,COMMAND_RANGE_HANDLER和COMMAND_RANGE_CODE_HANDLER。
通知消息处理宏有五个——NOTIFY_HANDLER,NOTIFY_ID_HANDLER,NOTIFY_CODE_HANDLER,NOTIFY_RANGE_HANDLER和NOTIFY_RANGE_CODE_HANDLER
简要看下MESSAGE_HANDLER和MESSAGE_RANGE_HANDLER两个宏的定义:
MESSAGE_HANDLER(msg, func)
消息处理函数通常是下面这样的:
LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 参数bHandled的作用是该处理函数是否处理该消息。如果它为FALSE,消息MAP的其它处理函数会来处理这个消息。
注意:在ATL消息处理函数中并没有将消息参数wParam,lParam展开,在后面我将解释在WTL中最终解决这问题。
到此处,大致了解ATL通过在派生类中定义消息映射宏和消息处理宏对相应的消息作处理,那么对于那些未处理的消息有时怎样交给基类处理的呢?即窗口处理函数逻辑中怎样把派生类与基类的ProcessWindowMessage()连起来呢?有一个宏CHAIN_MSG_MAP()。它的作用就是把两个类对消息的处理连起来。
#define CHAIN_MSG_MAP(theChainClass) \
{ \
if(theChainClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) \
return TRUE; \
} 很简单,它仅仅调用了基类的ProcessWindowMessage()函数。
至此,在了解ATL对Windows窗口的封装之后,我将建立一个ATL应用程序来示例上面所述:
(注:此例参考“MFC程序员的WTL指南”一书示例)
步骤1:新建win32 Application。工程名:AtlTest
在StaAfx.h中加上:
#include <atlbase.h> // 基本的ATL类
extern CComModule _Module; // 全局_Module
#include <atlwin.h> // ATL窗口类 步骤2:定义窗口类CMyWindow
// MyWindow.h Code // MyWindow.cpp
Code 其中CPaintBkgnd为一嵌入类,此类在OnEraseBkgnd函数中获取模板参数子类T的指针改变子类的窗口背景色。
Code 利用资源编辑器添加一个对话框资源IDD_ABOUT,也创建一个对话框类CAboutDlg:
// AboutDlg.h
Code // AboutDlg.cpp
Code 注意:ATL中任何非对话框窗口都是从CWindowImpl 派生,而对话框窗口类则从CDialogImpl或CAxDialogImpl
派生。
步骤3:应用程序主函数及消息循环
// AtlTest.cpp
Code 程序运行如图:
二:WTL对Windows窗口的封装的一些改进
前面分析ATL对于普通消息的宏处理如:MESSAGE_HANDLER(WM_CREATE, OnCreate)
关联的消息处理函数原型通常是:(WM_COMMAND和WM_NOTIFY消息除外)
LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 这里消息函数得到的参数是原始的WPARAM 和 LPARAM值,你需要自己将其展开为相应的消息所需要的参数。
WTL则利用新的消息映射宏和消息处理宏很好的解决这个问题。
这些宏是:BEGIN_MSG_MAP_EX 以及一系列对应普通消息的处理宏MSG_WM_CREATE、MSG_WM_DESTROY等等。。。
这些宏的定义在atlcrack.h中:
Code 消息处理宏以MSG_WM_CREATE为例
1 #define MSG_WM_CREATE(func) \
2 if (uMsg == WM_CREATE) \
3 { \
4 SetMsgHandled(TRUE); \
5 lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \
6 if(IsMsgHandled()) \
7 return TRUE; \
8 } 很清晰的得知对于WM_CREATE的处理函数只含有一个参数类型为LPCREATESTRUCT的参数。对于手动添加普通消息的处理,消息处理函数声明可以通过在atlcrack.h中查询到对应的消息处理宏就可得知函数的原型。
来源:https://www.cnblogs.com/vsense/archive/2009/07/28/1533435.html