天天看点

Windows程序设计-鼠标

基础

确认鼠标是否存在

fMouse = GetSystemMetrics (SM_MOUSEPRESENT); // True(非0)存在      

确定按键个数

cButtons = GetSystemMetrics (SM_CMOUSEBUTTONS) ;      

双击速度SystemParametersInfo

鼠标按键动作

  • Clicking按下并放开一个鼠标按键。
  • Double-clicking快速按下并放开鼠标按键两次。
  • Dragging按住鼠标按键并移动鼠标。

对三键鼠标来说,三个键分别称为左键、中键、右键。在Windows表头文件中定义的与鼠标有关的标识符使用缩写LBUTTON、MBUTTON和RBUTTON。

显示区域鼠标消息

只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。Windows为鼠标定义了21种消息,不过,其中有11个消息和显示区域无关(下面称之为「非显示区域」消息),Windows程序经常忽略这些消息。

只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。Windows为鼠标定义了21种消息,不过,其中有11个消息和显示区域无关(下面称之为「非显示区域」消息),Windows程序经常忽略这些消息。

当鼠标移过窗口的显示区域时,窗口消息处理程序收到WM_MOUSEMOVE*消息。当在窗口的显示区域中按下或者释放一个鼠标按键时,窗口消息处理程序会接收到下面这些消息:

按下 释放 按下(双键)
WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK
WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCLK
WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK

对于所有这些消息来说,其lParam值均含有鼠标的位置:低字组为x坐标,高字组为y坐标,这两个坐标是相对于窗口显示区域左上角的位置。您可以用LOWORD和HIWORD宏来提取这些值:

x = LOWORD (lParam) ;
y = HIWORD (lParam) ;      

wParam的值指示鼠标按键以及Shift和Ctrl键的状态。您可以使用表头文件WINUSER.H中定义的位屏蔽来测试wParam。MK前缀代表鼠标按键。

  • MK_LBUTTON 按下左键
  • MK_MBUTTON 按下中键
  • MK_RBUTTON 按下右键
  • MK_SHIFT 按下Shift键
  • MK_CONTROL 按下Ctrl键

例如,如果收到了WM_LBUTTONDOWN消息,而且值

wparam & MK_SHIFT      

是TRUE(非0),您就知道当左键按下时也按下了Shift键。

如果希望您的窗口消息处理程序能够收到双按键的鼠标消息,那么在呼叫RegisterClass初始化窗口类别结构时,必须在窗口风格中包含CS_DBLCLKS标识符:

wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;      

如果您的窗口类别风格中包含了CS_DBLCLKS,那么双击时窗口消息处理程序将收到如下消息:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_LBUTTONUP

WM_LBUTTONDBLCLK消息简单地替换了第二个WM_LBUTTONDOWN消息。

非显示区域鼠标消息

如果鼠标在窗口的显示区域之外但还在窗口内,Windows就给窗口消息处理程序发送一条「非显示区域」鼠标消息。窗口非显示区域包括标题列、菜单和窗口滚动条。

非显示区域鼠标消息几乎完全与显示区域鼠标消息相对应。消息中含有字母「NC」以表示是非显示区域消息。如果鼠标在窗口的非显示区域中移动,那么窗口消息处理程序会接收到WM_NCMOUSEMOVE消息。

按下 释放 按下(双击)
WM_NCLBUTTONDOWN WM_NCLBUTTONUP WM_NCLBUTTONDBLCLK
WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK

对非显示区域鼠标消息,wParam和lParam参数与显示区域鼠标消息的wParam和lParam参数不同。wParam参数指明移动或者按鼠标按键的非显示区域。它设定为WINUSER.H中定义的以HT开头的标识符之一(HT表示命中测试)。

lParam参数的低位word为x坐标,高位word为y坐标,但是,它们是屏幕坐标,而不是像显示区域鼠标消息那样指的是显示区域坐标。对屏幕坐标,显示器左上角的x和y的值为0。当往右移时x的值增加,往下移时y的值增加。

可以用两个Windows函数将屏幕坐标转换为显示区域坐标或者反之:

POINT pt;
ScreenToClient (hwnd, &pt) ;
ClientToScreen (hwnd, &pt) ;      

第21个鼠标消息:命中测试消息WM_NCHITTEST,代表非显示区域命中测试。此消息优先于所有其它的显示区域和非显示区域鼠标消息。lParam参数含有鼠标位置的x和y屏幕坐标,wParam参数没有用。

Windows 应 用 程 序 通 常 把 这 个 消 息 传 送 给 DefWindowProc , 然 后 Windows 用WM_NCHITTEST消息产生与鼠标位置相关的所有其它鼠标消息。对于非显示区域鼠标消息,在处理WM_NCHITTEST时,从DefWindowProc传回的值将成为鼠标消息中的wParam参数,这个值可以是任意非显示区域鼠标消息的wParam值再加上以下内容:

  • HTCLIENT 显示区域
  • HTNOWHERE 不在窗口中
  • HTTRANSPARENT 窗口由另一个窗口覆盖
  • HTERROR 使DefWindowProc产生警示用的哔声

如果DefWindowProc在其处理WM_NCHITTEST消息后传回HTCLIENT,那么Windows将把屏幕坐标转换为显示区域坐标并产生显示区域鼠标消息。

命中测试例子

/*-------------------------------------------------------------------------
CHECKER1.C -- Mouse Hit-Test Demo Program No. 1
(c)  Charles  Petzold,
--------------------------------------------------------------------------*/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR
    szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Checker1");
    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;
    }
    hwnd = CreateWindow(szAppName, TEXT("Checker1 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 BOOL fState[DIVISIONS][DIVISIONS];
    static int cxBlock, cyBlock;
    HDC hdc;
    int x, y;
    PAINTSTRUCT ps;
    RECT rect;
    switch (message)
    {
    case WM_SIZE:
        cxBlock = LOWORD(lParam) / DIVISIONS;
        cyBlock = HIWORD(lParam) / DIVISIONS;
        return 0;
    case WM_LBUTTONDOWN:
        x = LOWORD(lParam) / cxBlock;
        y = HIWORD(lParam) / cyBlock;
        if (x < DIVISIONS && y < DIVISIONS)
        {
            fState[x][y] ^= 1;
            rect.left = x *
                cxBlock;
            rect.top = y *
                cyBlock;
            rect.right = (x + 1) *
                cxBlock;
            rect.bottom = (y + 1) *
                cyBlock;
            InvalidateRect(hwnd, &rect, FALSE);
        }
        else
            MessageBeep(0);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        for (x = 0; x < DIVISIONS; x++)
        for (y = 0; y < DIVISIONS; y++)
        {
            Rectangle(hdc, x * cxBlock, y * cyBlock,
                (x + 1) * cxBlock, (y + 1) * cyBlock);
            if (fState[x][y])
            {
                MoveToEx(hdc, x * cxBlock, y * cyBlock,
                    NULL);
                LineTo(hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);
                MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);
                LineTo(hdc, (x + 1) * cxBlock, y * cyBlock);
            }
        }
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return      

加入键盘接口

Windows程序设计-鼠标
/*----------------------------------------------------------------------------
CHECKER2.C -- Mouse Hit-Test Demo Program No. 2
(c) Charles Petzold, 1998
----------------------------------------------------------------------------*/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Checker2");
    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;
    }
    hwnd = CreateWindow(szAppName, TEXT("Checker2 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 BOOL fState[DIVISIONS][DIVISIONS];
    static int cxBlock, cyBlock;
    HDC hdc;
    int x, y;
    PAINTSTRUCT ps;
    POINT point;
    RECT rect;
    switch (message)
    {
    case WM_SIZE:
        cxBlock = LOWORD(lParam) / DIVISIONS;
        cyBlock = HIWORD(lParam) / DIVISIONS;
        return 0;
    case WM_SETFOCUS:
        ShowCursor(TRUE);
        return 0;
    case WM_KILLFOCUS:
        ShowCursor(FALSE);
        return 0;
    case WM_KEYDOWN:
        GetCursorPos(&point);
        ScreenToClient(hwnd, &point);
        x = max(0, min(DIVISIONS - 1, point.x / cxBlock));
        y = max(0, min(DIVISIONS - 1, point.y / cyBlock));
        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;
        case VK_RETURN:
        case VK_SPACE:
            SendMessage(hwnd,
                WM_LBUTTONDOWN, MK_LBUTTON,
                MAKELONG(x * cxBlock, y * cyBlock));
            break;
        }
        x = (x + DIVISIONS) % DIVISIONS;
        y = (y + DIVISIONS) % DIVISIONS;
        point.x = x * cxBlock + cxBlock / 2;
        point.y = y * cyBlock + cyBlock / 2;
        ClientToScreen(hwnd, &point);
        SetCursorPos(point.x, point.y);
        return 0;
    case WM_LBUTTONDOWN:
        x = LOWORD(lParam) / cxBlock;
        y = HIWORD(lParam) / cyBlock;
        if (x < DIVISIONS && y < DIVISIONS)
        {
            fState[x][y] ^= 1;
            rect.left = x * cxBlock;
            rect.top = y * cyBlock;
            rect.right = (x + 1) *
                cxBlock;
            rect.bottom = (y + 1) *
                cyBlock;
            InvalidateRect(hwnd, &rect,
                FALSE);
        }
        else
            MessageBeep(0);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        for (x = 0; x < DIVISIONS; x++)
        for (y = 0; y < DIVISIONS; y++)
        {
            Rectangle(hdc, x * cxBlock, y * cyBlock,
                (x + 1) * cxBlock, (y + 1) * cyBlock);
            if (fState[x][y])
            {
                MoveToEx(hdc, x *cxBlock, y *cyBlock, NULL);
                LineTo(hdc, (x + 1)*cxBlock,
                    (y + 1)*cyBlock);
                MoveToEx(hdc, x  *cxBlock,
                    (y + 1)*cyBlock, NULL);
                LineTo(hdc, (x + 1)*cxBlock, y *cyBlock);
            }
        }
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return      

子窗口

/*---------------------------------------------------------------------------
CHECKER3.C -- Mouse Hit-Test Demo Program No. 3
(c) Charles Petzold, 1998
----------------------------------------------------------------------------*/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT
("Checker3_Child");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int
    iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Checker3");
    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("Checker3 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(0);
        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); // on/off flag
        return 0;
    case WM_LBUTTONDOWN:
        SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
        InvalidateRect(hwnd, NULL, FALSE);
        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);
        }
        EndPaint(hwnd, &ps);
        return 0;
    }
    return      

在WinMain中CreateWindow呼叫的参数,与在建立25个子窗口的WndProc中CreateWindow呼叫的参数间的比较:

参数 主窗口 子窗口
窗口类别 「Checker3」 「Checker3_Child」
窗口标题 「Checker3…」 NULL
窗口样式 WS_OVERLAPPEDWINDOW

​WS_CHILDWINDOW |WS_VISIBLE​

水平位置 CW_USEDEFAULT
垂直位置 CW_USEDEFAULT
宽度 CW_USEDEFAULT
高度 CW_USEDEFAULT
父窗口句柄 NULL hwnd
菜单句柄/子ID NULL

​(HMENU) (y << 8 | x)​

执行实体句柄 hInstance

​(HINSTANCE)GetWindowLong (hwnd, GWL_HINSTANCE)​

额外参数 NULL NULL

子窗口和键盘

Windows程序设计-鼠标
/*---------------------------------------------------------------------------
CHECKER4.C -- Mouse Hit-Test Demo Program No. 4
(c) Charles Petzold, 1998
---------------------------------------------------------------------------*/
#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(0);
        return 0;
        // On set-focus message, set focus to child window
    case WM_SETFOCUS:
        SetFocus(GetDlgItem(hwnd, idFocus));
        return 0;
        // On key-down message, possibly change the focus window
    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); // on/off flag
        return 0;
    case WM_KEYDOWN:
        // Send most key presses to the parent window
        if (wParam != VK_RETURN && wParam != VK_SPACE)
        {
            SendMessage(GetParent(hwnd), message, wParam,
                lParam);
            return 0;
        }
        // For Return and Space, fall through to toggle the square
    case WM_LBUTTONDOWN:
        SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
        SetFocus(hwnd);
        InvalidateRect(hwnd, NULL, FALSE);
        return 0;
        // For focus messages, invalidate the window for repaint
    case WM_SETFOCUS:
        idFocus = GetWindowLong(hwnd, GWL_ID);
        // Fall through
    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);
        // Draw the "x" mark
        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);
        }
        // Draw the "focus" rectangle
        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      

拦截鼠标

Windows程序设计-鼠标
/*----------------------------------------------------------------------------
BLOKOUT2.C -- Mouse Button & Capture Demo Program
(c) Charles Petzold, 1998
----------------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("BlokOut2");
    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;
    }
    hwnd = CreateWindow(szAppName, TEXT("Mouse Button & Capture 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;
}
void DrawBoxOutline(HWND hwnd, POINT ptBeg, POINT ptEnd)
{
    HDC hdc;
    hdc = GetDC(hwnd);
    SetROP2(hdc, R2_NOT);
    SelectObject(hdc, GetStockObject(NULL_BRUSH));
    Rectangle(hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y);
    ReleaseDC(hwnd, hdc);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM
    wParam, LPARAM lParam)
{
    static BOOL fBlocking, fValidBox;
    static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd;
    HDC hdc;
    PAINTSTRUCT ps;
    switch (message)
    {
    case WM_LBUTTONDOWN:
        ptBeg.x = ptEnd.x = LOWORD(lParam);
        ptBeg.y = ptEnd.y = HIWORD(lParam);
        DrawBoxOutline(hwnd, ptBeg, ptEnd);
        SetCapture(hwnd);
        SetCursor(LoadCursor(NULL, IDC_CROSS));
        fBlocking = TRUE;
        return 0;
    case WM_MOUSEMOVE:
        if (fBlocking)
        {
            SetCursor(LoadCursor(NULL, IDC_CROSS));
            DrawBoxOutline(hwnd, ptBeg, ptEnd);
            ptEnd.x = LOWORD(lParam);
            ptEnd.y = HIWORD(lParam);
            DrawBoxOutline(hwnd, ptBeg, ptEnd);
        }
        return 0;
    case WM_LBUTTONUP:
        if (fBlocking)
        {
            DrawBoxOutline(hwnd, ptBeg, ptEnd);
            ptBoxBeg = ptBeg;
            ptBoxEnd.x = LOWORD
                (lParam);
            ptBoxEnd.y = HIWORD
                (lParam);
            ReleaseCapture();
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            fBlocking = FALSE;
            fValidBox = TRUE;
            InvalidateRect(hwnd, NULL, TRUE);
        }
        return 0;
    case WM_CHAR:
        if (fBlocking & wParam == '\x1B') // i.e., Escape
        {
            DrawBoxOutline(hwnd, ptBeg, ptEnd);
            ReleaseCapture();
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            fBlocking = FALSE;
        }
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (fValidBox)
        {
            SelectObject(hdc, GetStockObject
                (BLACK_BRUSH));
            Rectangle(hdc, ptBoxBeg.x, ptBoxBeg.y,
                ptBoxEnd.x, ptBoxEnd.y);
        }
        if (fBlocking)
        {
            SetROP2(hdc, R2_NOT);
            SelectObject(hdc, GetStockObject
                (NULL_BRUSH));
            Rectangle(hdc, ptBeg.x, ptBeg.y, ptEnd.x,
                ptEnd.y);
        }
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return      
SetCapture (hwnd) ;      

在这个函数调用之后,Windows将所有鼠标消息发给窗口句柄为hwnd的窗口消息处理程序。之后收到鼠标消息都是以显示区域消息的型态出现,即使鼠标正在窗口的非显示区域。lParam参数将指示鼠标在显示区域坐标中的位置。不过,当鼠标位于显示区域的左边或者上方时,这些x和y坐标可以是负的。当您想释放鼠标时,调用:

ReleaseCapture () ;      

鼠标滑轮