WTL学习记(二):WTL的消息处理链

前小节简单介绍了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的定义是:      
1 class  ATL_NO_VTABLE CMessageMap
2 ExpandedBlockStart.gifContractedBlock.gif {
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()结束。
ContractedBlock.gif ExpandedBlockStart.gif #define BEGIN_MSG_MAP(theClass)
 1#define BEGIN_MSG_MAP(theClass) \
 2public: \
 3    BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \
 4ExpandedBlockStart.gifContractedBlock.gif    { \
 5        BOOL bHandled = TRUE; \
 6        hWnd; \
 7        uMsg; \
 8        wParam; \
 9        lParam; \
10        lResult; \
11        bHandled; \
12        switch(dwMsgMapID) \
13ExpandedSubBlockStart.gifContractedSubBlock.gif        { \
14        case 0:
      
ContractedBlock.gif ExpandedBlockStart.gif Code
1#define END_MSG_MAP() \
2            break; \
3        default: \
4            ATLTRACE2(atlTraceWindowing, 0, _T("Invalid message map ID (%i)\n"), dwMsgMapID); \
5            ATLASSERT(FALSE); \
6            break; \
7        } \
8        return FALSE; \
9    }
      显然,这是函数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两个宏的定义:
ContractedBlock.gif ExpandedBlockStart.gif MESSAGE_HANDLER(msg, func)
 1#define MESSAGE_HANDLER(msg, func) \
 2    if(uMsg == msg) \
 3ExpandedBlockStart.gifContractedBlock.gif    { \
 4        bHandled = TRUE; \
 5        lResult = func(uMsg, wParam, lParam, bHandled); \
 6        if(bHandled) \
 7            return TRUE; \
 8    }

 9
10#define MESSAGE_RANGE_HANDLER(msgFirst, msgLast, func) \
11    if(uMsg >= msgFirst && uMsg <= msgLast) \
12ExpandedBlockStart.gifContractedBlock.gif    { \
13        bHandled = TRUE; \
14        lResult = func(uMsg, wParam, lParam, bHandled); \
15        if(bHandled) \
16            return TRUE; \
17    }
      可见宏中只是要将一个消息(或一定范围内的消息)和一个消息处理函数连在一起。
      消息处理函数通常是下面这样的:
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
ContractedBlock.gif ExpandedBlockStart.gif Code
 1typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,WS_EX_APPWINDOW> CMyWindowTraits;
 2
 3class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits>,
 4                  public CPaintBkgnd<CMyWindow, RGB(0,255,0)>
 5ExpandedBlockStart.gifContractedBlock.gif{
 6public:
 7    CMyWindow();
 8    virtual ~CMyWindow();
 9
10    DECLARE_WND_CLASS(_T("My Window Class"))
11
12    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
13    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
14    LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
15
16    typedef CPaintBkgnd<CMyWindow, RGB(0,255,0)> CPaintBkgndBase;
17    BEGIN_MSG_MAP(CMyWindow)
18        MESSAGE_HANDLER(WM_CREATE, OnCreate)
19        COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
20        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
21        MESSAGE_HANDLER(WM_CLOSE, OnClose)
22        CHAIN_MSG_MAP(CPaintBkgndBase);
23    END_MSG_MAP()    
24    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
25}
;
      // MyWindow.cpp
ContractedBlock.gif ExpandedBlockStart.gif Code
 1LRESULT CMyWindow::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    PostQuitMessage(0);    
 4    return 0;
 5}

 6
 7LRESULT CMyWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 8ExpandedBlockStart.gifContractedBlock.gif{
 9    DestroyWindow();
10    return 0;
11}

12LRESULT CMyWindow::OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
13ExpandedBlockStart.gifContractedBlock.gif{
14//     MessageBox ( _T("Sample ATL window"), _T("About MyWindow") );
15    CAboutDlg dlgAbout;
16    dlgAbout.DoModal();
17    return 0;
18}

19
20LRESULT CMyWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
21ExpandedBlockStart.gifContractedBlock.gif{
22    HMENU hMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MENU));
23    SetMenu(hMenu);
24    return 0;
25}
      其中CPaintBkgnd为一嵌入类,此类在OnEraseBkgnd函数中获取模板参数子类T的指针改变子类的窗口背景色。
ContractedBlock.gif ExpandedBlockStart.gif Code
 1template <class T, COLORREF t_crBrushColor>
 2class CPaintBkgnd : public CMessageMap  
 3ExpandedBlockStart.gifContractedBlock.gif{
 4public:
 5    CPaintBkgnd() 
 6ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 7        m_hbrBkgnd = CreateSolidBrush(t_crBrushColor);
 8    }

 9    virtual ~CPaintBkgnd()
10ExpandedSubBlockStart.gifContractedSubBlock.gif    {
11        if (m_hbrBkgnd != NULL)
12ExpandedSubBlockStart.gifContractedSubBlock.gif        {
13            DeleteObject(m_hbrBkgnd);
14            m_hbrBkgnd = NULL;
15        }

16    }

17    
18    LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
19ExpandedSubBlockStart.gifContractedSubBlock.gif    {
20        T*   pT = static_cast<T*>(this);
21        HDC  dc = (HDC) wParam;
22        RECT rcClient;
23        
24        pT->GetClientRect ( &rcClient );
25        FillRect ( dc, &rcClient, m_hbrBkgnd );
26        return 1;    // we painted the background
27    //    return 0;
28    }

29
30    BEGIN_MSG_MAP(CPaintBkgnd)
31        MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
32    END_MSG_MAP()
33    
34protected:
35    HBRUSH m_hbrBkgnd;
36}
;
      利用资源编辑器添加一个对话框资源IDD_ABOUT,也创建一个对话框类CAboutDlg:
      // AboutDlg.h
ContractedBlock.gif ExpandedBlockStart.gif Code
 1class CAboutDlg : public CDialogImpl<CAboutDlg>
 2ExpandedBlockStart.gifContractedBlock.gif{
 3public:
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    enum {IDD = IDD_ABOUT};
 5    
 6    BEGIN_MSG_MAP(CAboutDlg)
 7        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
 8        MESSAGE_HANDLER(WM_CLOSE, OnClose)
 9        COMMAND_ID_HANDLER(IDOK, OnOkCancel)
10        COMMAND_ID_HANDLER(IDCANCEL, OnOkCancel)
11    END_MSG_MAP()    
12    LRESULT OnOkCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
13    LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
14    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
15}
;
      // AboutDlg.cpp
ContractedBlock.gif ExpandedBlockStart.gif Code
 1LRESULT CAboutDlg::OnOkCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    EndDialog(wID);
 4    return 0;
 5}

 6
 7LRESULT CAboutDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 8ExpandedBlockStart.gifContractedBlock.gif{
 9    CenterWindow();
10    return 0;
11}

12
13LRESULT CAboutDlg::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
14ExpandedBlockStart.gifContractedBlock.gif{
15    EndDialog(IDCANCEL);
16    return 0;

17
      注意:ATL中任何非对话框窗口都是从CWindowImpl 派生,而对话框窗口类则从CDialogImpl或CAxDialogImpl
派生。
      步骤3:应用程序主函数及消息循环
      // AtlTest.cpp
      
ContractedBlock.gif ExpandedBlockStart.gif Code
 1CComModule _Module;
 2
 3int APIENTRY WinMain(HINSTANCE hInstance,
 4                     HINSTANCE hPrevInstance,
 5                     LPSTR     lpCmdLine,
 6                     int       nCmdShow)
 7ExpandedBlockStart.gifContractedBlock.gif{
 8     // TODO: Place code here.
 9    _Module.Init(NULL, hInstance);
10    CMyWindow wndMain;
11    MSG msg;
12    
13    if (NULL == wndMain.Create(NULL, CWindow::rcDefault, _T("My First ATL Window")))
14ExpandedSubBlockStart.gifContractedSubBlock.gif    {
15        // Failed
16        return 1;
17    }

18    wndMain.ShowWindow(nCmdShow);
19    wndMain.UpdateWindow();
20
21    while (GetMessage(&msg, NULL, 00> 0)
22ExpandedSubBlockStart.gifContractedSubBlock.gif    {
23        TranslateMessage(&msg);
24        DispatchMessage(&msg);
25    }

26
27    _Module.Term();
28    
29    return msg.wParam;
30//     return 0;
31}
      程序运行如图:
      

二: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中:   
ContractedBlock.gif ExpandedBlockStart.gif Code
 1#define BEGIN_MSG_MAP_EX(theClass) \
 2public: \
 3    BOOL m_bATL3MsgHandled; \
 4ExpandedBlockStart.gifContractedBlock.gif    /**//* "handled" management for cracked handlers */ \
 5    BOOL IsMsgHandled() const \
 6ExpandedBlockStart.gifContractedBlock.gif    { \
 7        return m_bATL3MsgHandled; \
 8    }
 \
 9    void SetMsgHandled(BOOL bHandled) \
10ExpandedBlockStart.gifContractedBlock.gif    { \
11        m_bATL3MsgHandled = bHandled; \
12    }
 \
13    BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \
14ExpandedBlockStart.gifContractedBlock.gif    { \
15        BOOL bATL3OldMsgHandled = m_bATL3MsgHandled; \
16        BOOL bRet = _ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \
17        m_bATL3MsgHandled = bATL3OldMsgHandled; \
18        return bRet; \
19    }
 \
20    BOOL _ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID) \
21ExpandedBlockStart.gifContractedBlock.gif    { \
22        BOOL bHandled = TRUE; \
23        hWnd; \
24        uMsg; \
25        wParam; \
26        lParam; \
27        lResult; \
28        bHandled; \
29        switch(dwMsgMapID) \
30ExpandedSubBlockStart.gifContractedSubBlock.gif        { \
31        case 0:
      消息处理宏以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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值