7.2 客户区鼠标消息

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

 

按下

释放

按下(双键)

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_MBUTTONDOWN

WM_MBUTTONUP

WM_MBUTTONDBLCLK

WM_RBUTTONDOWN

WM_RBUTTONUP

WM_RBUTTONDBLCLK

 

要接受到双键消息,需要在wndclass.style处增加CS_DBLCLKS

 

在WM_MOUSEMOVE中,可以用LOWORD和HIWORD宏来提取鼠标的位置:

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

 

wParam中包含信息。MK前缀代表「鼠标按键」。

MK_LBUTTON

按下左键

MK_MBUTTON

按下中键

MK_RBUTTON

按下右键

MK_SHIFT

按下Shift键

MK_CONTROL

按下Ctrl键

 

当您把鼠标移过窗口的显示区域时,Windows并不为鼠标的每个可能的图素位置都产生一个WM_MOUSEMOVE消息。您的程序接收到WM_MOUSEMOVE消息的次数,依赖于鼠标硬件,以及您的窗口消息处理程序在处理鼠标移动消息时的速度。

 

  1. 窗口消息处理程序可以「拦截鼠标」并且连续地接收鼠标消息,即使此时鼠标在该窗口显示区域之外。您将在本章的后面学习如何拦截鼠标。
  2. 如果正在显示一个系统模态消息框或者系统模态对话框,那么其它程序就不能接收鼠标消息。当系统模态消息框或者对话框活动时,禁止切换到其它窗口或者程序。一个显示系统模态消息框的例子,是当您关闭Windows时。

 

实例

#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("Mouse Move");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
        return 0;
    }

    hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
        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 POINT pt[1000];
    HDC hdc;
    static int iCount;
    PAINTSTRUCT ps;
    int i, j;

    switch (message) {
    case WM_LBUTTONDOWN:
        iCount = 0;
        InvalidateRect(hwnd, NULL, TRUE);//重画时擦除背景
        return 0;
    case WM_MOUSEMOVE:
        if (wParam && MK_LBUTTON && iCount < 1000) {
            pt[iCount].x = LOWORD(lParam);
            pt[iCount++].y = HIWORD(lParam);
            hdc = GetDC(hwnd);
            SetPixel(hdc, LOWORD(lParam), HIWORD(lParam), 0);
            ReleaseDC(hwnd, hdc);
        }
        return 0;
    case WM_LBUTTONUP:
        InvalidateRect(hwnd, NULL, FALSE);//重画时不擦除背景
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        SetCursor(LoadCursor(NULL, IDC_WAIT));
        ShowCursor(TRUE);//显示鼠标
        for (i = 0; i < iCount - 1; ++i) {
            for (j = i + 1; j < iCount; ++j) {
                MoveToEx(hdc, pt[i].x, pt[i].y, NULL);
                LineTo(hdc, pt[j].x, pt[j].y);
            }
        }
        SetCursor(LoadCursor(NULL, IDC_ARROW));
        ShowCursor(FALSE);//隐藏鼠标
        EndPaint(hwnd, &ps);
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标_屏幕坐标

 

7.2.2 处理shift

组合键测试

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_MOUSEMOVE:
        if (wParam & MK_CONTROL) {
            if (wParam & MK_SHIFT) {
                MessageBox(hwnd, TEXT("按下Ctrl+Shift键"), TEXT("test!"), NULL);
                return 0;
            }
        }
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标_屏幕坐标_02

 

7.2.3 鼠标双击

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_LBUTTONDBLCLK:
        MessageBox(hwnd, TEXT("左键双击"), TEXT("test!"), NULL);
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标_sed_03

 

客户区鼠标消息简单应用

#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("Mouse Move");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
        return 0;
    }

    hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        600, 600,
        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 int cxClient, cyClient;
    static POINT point;
    HDC hdc;
    PAINTSTRUCT ps;
    TCHAR szBuffer[100];

    switch (message) {
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_MOUSEMOVE:
        GetCursorPos(&point);
        InvalidateRect(hwnd, NULL, TRUE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        TextOut(hdc, cxClient / 2 - 30, cyClient / 2 - 30, szBuffer, wsprintf(szBuffer, TEXT("屏幕坐标(%d, %d)"), point.x, point.y));
        ScreenToClient(hwnd, &point);
        TextOut(hdc, cxClient / 2 - 30, cyClient / 2 , szBuffer, wsprintf(szBuffer, TEXT("客户区坐标(%d, %d)"), point.x, point.y));
        EndPaint(hwnd, &ps);
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标_处理程序_04

 

7.3 非客户区鼠标消息

按下

释放

按下(双击)

WM_NCLBUTTONDOWN

WM_NCLBUTTONUP

WM_NCLBUTTONDBLCLK

WM_NCMBUTTONDOWN

WM_NCMBUTTONUP

WM_NCMBUTTONDBLCLK

WM_NCRBUTTONDOWN

WM_NCRBUTTONUP

WM_NCRBUTTONDBLCLK

 

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

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

 

7.3.1 击中测试消息

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将把屏幕坐标转换为显示区域坐标并产生显示区域鼠标消息。

 

#define HTERROR             (-2)                    //在屏幕的后面或在窗体之间的线上(使函数DefWindowProc产生一个警示音)
#define HTTRANSPARENT       (-1)                    //在一个被其它窗口覆盖的窗口中
#define HTNOWHERE           0                        //在屏幕背景或窗口之间的分界线
#define HTCLIENT            1                        //在客户区中
#define HTCAPTION           2                        //在标题栏中
#define HTSYSMENU           3                        //在一个窗口菜单栏或子窗口的关闭按钮上
#define HTGROWBOX           4                        //在尺寸框中
#define HTSIZE              HTGROWBOX                //同HTGROWBOX
#define HTMENU              5                        //在菜单区域
#define HTHSCROLL           6                        //在水平滚动条上
#define HTVSCROLL           7                        //在垂直滚动条上
#define HTMINBUTTON         8                        //在最小化按钮上
#define HTMAXBUTTON         9                        //在最大化按钮上
#define HTLEFT              10                        //在窗口的左边框上
#define HTRIGHT             11                        //在窗口的右边框上
#define HTTOP               12                        //在窗口水平边框的上方
#define HTTOPLEFT           13                        //在窗口边框的左上角
#define HTTOPRIGHT          14                        //在窗口边框的右上角
#define HTBOTTOM            15                        //在窗口的水平边框的底部
#define HTBOTTOMLEFT        16                        //在窗口边框的左下角
#define HTBOTTOMRIGHT       17                        //在窗口边框的右下角
#define HTBORDER            18                        //在不具有可变大小边框的窗口的边框上
#define HTREDUCE            HTMINBUTTON                //同HTMINBUTTON
#define HTZOOM              HTMAXBUTTON                //同HTMAXBUTTON
#define HTSIZEFIRST         HTLEFT                    //同HTLEFT
#define HTSIZELAST          HTBOTTOMRIGHT             //同HTBOTTOMRIGHT
#define HTOBJECT            19                        //忽略该标识符, 已废弃
#define HTCLOSE             20                        //在关闭按钮上
#define HTHELP              21                        //在帮助按钮上

 

#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("Mouse Move");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

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

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
        return 0;
    }

    hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        600, 600,
        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)
{
    TCHAR szBuffer[100];
    static POINT pt;

    switch (message) {
    case WM_NCLBUTTONDOWN:
        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);
        ScreenToClient(hwnd, &pt);
        switch (wParam) {
        case HTCAPTION:
            wsprintf(szBuffer, TEXT("击中标题栏,客户区坐标(%d, %d)"), pt.x, pt.y);
            MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
            break;
        case HTMINBUTTON:
            wsprintf(szBuffer, TEXT("击中最小化,客户区坐标(%d, %d)"), pt.x, pt.y);
            MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
            break;
        case HTMAXBUTTON:
            wsprintf(szBuffer, TEXT("击中最大化,客户区坐标(%d, %d)"), pt.x, pt.y);
            MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
            break;
        }
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标_ico_05