• [转]使用HANDLE_MSG宏简化Win32应用的开发


    Win32应用中的回调函数 WndProc 用于接收 Windows 向应用程序直接发送的消息,以及响应消息。大多情况下,我们这样编写代码:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) 
    {
        int cxClient, cyClient; PAINTSTRUCT ps; HDC hdc;
     
        switch( message ) 
        {
            case WM_SIZE: 
                cxClient = LOWORD(lParam); 
                cyClient = HIWORD(lParam); 
                break; 
    
            case WM_PAINT: 
                hdc = BeginPaint( hWnd, &ps ); 
                EndPaint( hWnd, &ps ); 
                break; 
    
            case WM_DESTROY: 
                PostQuitMessage( 0 ); 
                break; 
        }
        return DefWindowProc( hWnd, message, wParam, lParam ); 
    } 
    
    这种方式通过switch分支语句分理处理各个消息,结构简单明了。但有2个问题:
     
      1.在所处理的消息多了后,全部代码都集中于一个switch语句中,变量容易相互污染,且代码很长,不容易定位代码。
      2.大多数的消息都有相应的 WPARAM、LPARAM 参数,但对于每个消息,其 WPARAM、LPARAM 参数的具体内容又不一致。就像上面 WM_SIZE 中的代码,我们怎么知道 cyClient、cyClient 的信息就放在 lParam 而不是 wParam 中,且 cxClient 的信息在 lParam 的低位,cyClient 的信息在 lParam 的高位?我们需要随时查询 SDK 文档以了解这两个参数中各有什么含义,以及如何恰当地将这些参数抽取出来。不管怎样,上面的代码实际上就是个让人一团雾水的魔术代码(Magic Code)。
    WindowsX.h 定义了许多宏,可以帮助我们解决上述这些问题。其中 HANDLE_MSG 宏可以大大简化 Win32 开发。下面我们先看使用 HANDLE_MSG 宏后的代码:
    // 处理 WM_SIZE 消息.
    void OnSize(HWND hwnd, UINT state, int cx, int cy) 
    { }
    
    // 处理 WM_PAINT 消息.
    void OnPaint(HWND hWnd) 
    {
        PAINTSTRUCT ps; 
        HDC hdc; hdc = BeginPaint( hWnd, &ps ); 
        EndPaint( hWnd, &ps ); 
    }
    
    // 处理 WM_DESTROY 消息.
    void OnWinDestroy(HWND hWnd) 
    {
        PostQuitMessage(0); 
    } 
    
    // 主窗口过程.
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
    {
        switch(message) 
        {
            HANDLE_MSG(hWnd, WM_SIZE, OnSize); 
            HANDLE_MSG(hWnd, WM_PAINT, OnPaint); 
            HANDLE_MSG(hWnd, WM_DESTROY, OnWinDestroy); 
        }
        return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    
    与使用 HANDLE_MSG 宏之前的代码相比,我们根本不需使用 LOWORD、HIWORD 来提取 cxCleint 及 cyClient 的信息,OnSize 函数的参数已经有 cx 及 cy,而且还传进一个表示窗口变化类型的参数 state,如 SIZE_RESTORED, SIZE_MAXSHOW 等。ps 与 hdc 这两个变量移至 OnPaint 函数中而成为真正的局域变量。在 switch 分支语句中,对于每一个消息,都使用 HANDLE_MSG 宏清晰地将消息与消息处理函数对应起来,代码非常简洁。而无论是在消息处理函数,或是 switch 分支中,我们均无需显式地加上 break 语句。
    下面我们来看看 HANDLE_MSG 宏是如何做到这一点的。HANDLE_MSG 宏的定义如下所示:
    #define HANDLE_MSG(hwnd, message, fn) \
    case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) 
    
    HANDLE_MSG(hwnd, message, fn) 是宏的签名。hwnd 是需处理消息的窗口句柄,message 是消息,fn 是指向负责消息处理的函数指针。当编译器遇到此宏,将在编译时将其转换为:
    case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) 
    
    的形式。## 是 ANSIC 标准中的预处理器,用于将前后两个符号直接连接起来。因此,当 message 为 WM_SIZE 时,HANDLE_##message 就变成:
    HANDLE_WM_SIZE
    
    因此,对于
    HANDLE_MSG(hWnd, WM_SIZE, OnSize) 
    
    编译器将转换为:
    case WM_SIZE: return HANDLE_WM_SIZE(hwnd, wParam, lParam, OnSize) 
    
    我们注意到,尽管 HANDLE_MSG 宏中未使用 wParam 及 lParam 参数,但展开宏后,这两个参数均加进来了。
    HANDLE_WM_SIZE 也是一个在 WindowsX.h 中定义的宏,其原型如下:
    #define HANDLE_WM_SIZE(hwnd, wParam, lParam, fn) \
     ((fn)((hwnd), (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)), 0L)
    
    正是在 HANDLE_WM_SIZE 宏中,自动将 wParam, lParam 的高位及低位分别抽出,作为 OnSize 函数的实参进行传递。
    应注意,不同消息处理函数的参数列表是不一样的。如 OnSize 函数,其参数列表包括 HWND, UINT, int, int, 而OnPaint、OnWindDestory 函数均只有一个HWND参数。那么,如何声明这些消息处理函数的签名?很简单,在 WindowsX.h文件中找到定义 HANDLE_WM_SIZE 的地方,其上面有一行注释:
    /* void Cls_OnSize(HWND hwnd, UINT state, int cx, int cy) */
    #define HANDLE_WM_SIZE(hwnd, wParam, lParam, fn) \
     ((fn)((hwnd), (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)), 0L) 
    
     
    Cls_OnSize 函数已经给出了函数的签名,因此,我们只需将此签名复制过来,并将Cls_OnSize更名为自己选择的函数名称即可。
     
  • 相关阅读:
    Storm 中drpc调用
    yarn下资源配置
    java 中 Stringbuff append源代码浅析
    总结的MR中连接操作
    hive中使用rcfile
    MapFile
    HDFS副本存放读取
    zoj 1967 Fiber Network/poj 2570
    zoj 2027 Travelling Fee
    poj 1742 Coins
  • 原文地址:https://www.cnblogs.com/wxxweb/p/2093426.html
Copyright © 2020-2023  润新知