父窗口和子窗口的消息分发

《Windows 程序设计》第七章中的 Checker3 和 Checker4 程序

为父窗口和子窗口分别定义了窗口过程 WndProc 和 ChildWndProc

并且父窗口和子窗口使用同一个消息循环

Checker4 的代码如下

#include <windows.h>

#define DIVISIONS 5

LRESULT CALLBACK WndProc      (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ChildWndProc (HWND, UINT, WPARAM, LPARAM) ;

int      idFocus = 0 ;
TCHAR szChildClass[] = TEXT ("Checker4_Child") ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("Checker4") ;
    HWND         hwnd ;
    MSG             msg ;
    WNDCLASS     wndclass ;

    wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc   = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance ;
    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;

    if (!RegisterClass (&wndclass))
    {
        MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
            szAppName, MB_ICONERROR) ;
        return 0 ;
    }

    wndclass.lpfnWndProc   = ChildWndProc ;
    wndclass.cbWndExtra    = sizeof (long) ;
    wndclass.hIcon         = NULL ;
    wndclass.lpszClassName = szChildClass ;

    RegisterClass (&wndclass) ;

    hwnd = CreateWindow (szAppName, TEXT ("Checker4 Mouse Hit-Test Demo"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL) ;

    ShowWindow (hwnd, iCmdShow) ;
    UpdateWindow (hwnd) ;

    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
    return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND            hwndChild[DIVISIONS][DIVISIONS] ;
    int                    cxBlock, cyBlock, x, y ;
    
    switch (message)
    {
    case WM_CREATE:
        for (x = 0 ; x < DIVISIONS ; x++)
            for (y = 0 ; y < DIVISIONS ; y++)
                hwndChild[x][y] = CreateWindow (szChildClass, NULL,
                                                WS_CHILDWINDOW | WS_VISIBLE,
                                                0, 0, 0, 0,
                                                hwnd, (HMENU) (y <<8 | x),
                                                (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),
                                                 NULL) ;
        return 0 ;

    case WM_SIZE:
        cxBlock = LOWORD (lParam) / DIVISIONS ;
        cyBlock = HIWORD (lParam) / DIVISIONS ;

        for (x = 0 ; x < DIVISIONS ; x++)
            for (y = 0 ; y < DIVISIONS ; y++)
                MoveWindow (hwndChild[x][y], x * cxBlock, y * cyBlock,
                            cxBlock, cyBlock, TRUE) ;
        return 0 ;

    case WM_LBUTTONDOWN:
        MessageBeep (1) ;
        return 0 ;

    case WM_KEYDOWN:
        x = idFocus & 0xFF ;
        y = idFocus >> 8 ;

        switch (wParam)
        {
        case VK_UP:    y-- ;                    break ;
        case VK_DOWN:  y++ ;                    break ;
        case VK_LEFT:  x-- ;                    break ;
        case VK_RIGHT: x++ ;                    break ;
        case VK_HOME:  x = y = 0 ;              break ;
        case VK_END:   x = y = DIVISIONS - 1 ;  break ;
        default:       return 0 ;
        }

        x = (x + DIVISIONS) % DIVISIONS ;
        y = (y + DIVISIONS) % DIVISIONS ;

        idFocus = (y << 8) | x ;

        SetFocus (GetDlgItem (hwnd, idFocus)) ;    //???
        return 0 ;
        
    case WM_DESTROY:
        PostQuitMessage (0) ;
        return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

LRESULT CALLBACK ChildWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC            hdc ;
    PAINTSTRUCT ps ;
    RECT        rect ;

    switch (message)
    {
    case WM_CREATE:
        SetWindowLong (hwnd, 0, 0) ;
        return 0 ;

    case WM_KEYDOWN:
        if (wParam != VK_RETURN && wParam != VK_SPACE)
        {
            SendMessage (GetParent (hwnd), message, wParam, lParam) ;
            return 0 ;
        }

    case WM_LBUTTONDOWN:
        SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0)) ;
        SetFocus (hwnd) ;
        InvalidateRect (hwnd, NULL, FALSE) ;
        return 0 ;

    case WM_SETFOCUS:
        idFocus = GetWindowLong (hwnd, GWL_ID) ;

    case WM_KILLFOCUS:
        InvalidateRect (hwnd, NULL, TRUE) ;
        return 0 ;

    case WM_PAINT:
        hdc = BeginPaint (hwnd, &ps) ;

        GetClientRect (hwnd, &rect) ;
        Rectangle (hdc, 0, 0, rect.right, rect.bottom) ;

        if (GetWindowLong (hwnd, 0))
        {
            MoveToEx (hdc, 0,          0, NULL) ;
            LineTo   (hdc, rect.right, rect.bottom) ;
            MoveToEx (hdc, 0,          rect.bottom, NULL) ;
            LineTo   (hdc, rect.right, 0) ;
        }

        if (hwnd == GetFocus ())
        {
            rect.left    += rect.right / 10 ;
            rect.right    -= rect.left ;
            rect.top    += rect.bottom / 10 ;
            rect.bottom -= rect.top ;

            SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
            SelectObject (hdc, CreatePen (PS_DASH, 0, 0)) ;
            Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom) ;
            DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ;
        }

        EndPaint (hwnd, &ps) ;
        return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

于是对其父窗口和子窗口消息的分发产生了困惑

如当用户在客户区移动或点击鼠标时,相关消息发送给 WndProc 还是 ChildWndProc

当用户按下键盘按键时,相关消息发送给 WndProc 还是 ChildWndProc


 

针对这两种情况我分别进行了下跟踪调试,过程如下

首先,在两个窗口过程的消息处理中添加对 WM_MOUSEMOVE 消息的支持

    case WM_MOUSEMOVE:
        return  0 ;

然后对其设置断点,编译-链接-调试,在客户区移动鼠标进行测试

因为该程序在客户区定义了25个子窗口,以 5 * 5 的格子形分布

所以在客户区宽高不被5整除时,客户区的右侧或下侧会留下部分空白

如下图

首先在网格内移动鼠标,在 ChildWndProc 中的断点拦截到消息

然后在空白处移动鼠标,在 WndProc 中的断点拦截到消息

再进行单击的测试,取消 WM_MOUSEMOVE 的断点

对 WM_LBUTTONDOWN 消息设置断点

在网格内单击鼠标,ChildWndProc 中的断点拦截到消息

在空白处单击鼠标,WndProc 中的断点拦截到消息

由此可以推测,

Windows 会自动查找当前鼠标位置所归属的窗口句柄,并对其分发消息

假设其25个子窗口是平铺在客户区上,在鼠标经过时

主窗口的客户区已被子窗口客户区遮盖

所以此时窗口消息属于子窗口,由子窗口接收窗口消息


 

因为对消息分发不甚理解,所以该程序对 WM_KEYDOWN 的处理也是云里雾里

首先分别对两个窗口过程的消息处理中对 WM_KEYDOWN 消息设置断点

调试程序,任意按下一个按键

当第一次按键时,消息被分发到了 WndProc 窗口过程

之后的按键均先经过 ChildWndProc 窗口过程,如非空格和回车再回传给 WndProc

因为程序中有对输入焦点的设置,于是判断键盘消息的分发与输入焦点有关

在 WndProc 中对 WM_SIZE 消息处理中添加了一条语句

        SetFocus (GetDlgItem (hwnd, idFocus)) ;

重新编译调试,这次所有的键盘消息均先经过 ChildWndProc

最小化程序,然后再恢复窗口,键盘消息又被分发到了 WndProc

由此可以推测

键盘消息会被分发到当前具有输入焦点的窗口句柄中

或者说键盘消息会发生在当前具有输入焦点的窗口中

然后改进下程序因为失去焦点再恢复后导致的子窗口虚线丢失的问题

在 WndProc 的消息处理中添加 WM_SETFOCUS 消息的处理

    case WM_SETFOCUS:
        SetFocus (GetDlgItem (hwnd, idFocus)) ;
        return 0 ;

 

转载于:https://www.cnblogs.com/LShang/archive/2012/12/09/2810276.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值