当光标在窗口的客户区时,用户点击 鼠标 按钮,这个窗口可能会收到下面这些消息:

消息 含义
WM_LBUTTONDOWN Left button down
WM_LBUTTONUP Left button up
WM_MBUTTONDOWN Middle button down
WM_MBUTTONUP Middle button up
WM_RBUTTONDOWN Right button down
WM_RBUTTONUP Right button up
WM_XBUTTONDOWN XBUTTON1 or XBUTTON2 down
WM_XBUTTONUP XBUTTON1 or XBUTTON2 up

1. 鼠标坐标

在上述这些消息中,lParam 参数包含鼠标指针的位置坐标,低 16 位为 x 坐标,接下来连续 16 位为 y 坐标。使用 GET_X_LPARAM 和 GET_Y_LPARAM 两个宏可以获取坐标位置。

int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);

这些宏定义在 WindowsX.h 文件中。

在64位系统中,lParam 参数为64位,高32位并没有使用。MSDN 中用 “low-order word” 和 “high-order word” 描述 lParam 参数,但是在64位系统中,这个 low 和 high 的含义都是对于低32位来说的。如果你直接使用宏获取参数则总是安全的,否则你就需要注意你的解析参数的逻辑是否存在问题。

鼠标坐标指的是像素位置,不是 设备独立像素(DIPs),坐标体系是相对于客户区来建立的。坐标是有符号类型,客户区左上角为(0,0)点,而超过(0,0)点再往上或者左侧都是负坐标。

2. 附加标记

参数 wParam 包含了按位进行或运算的一些标记位。表明了鼠标的状态以及是否按下 SHIFT 和 CTRL 键。

标键 含义
MK_CONTROL The CTRL key is down
MK_LBUTTON The left mouse button is down.
MK_MBUTTON The middle mouse button is down.
MK_RBUTTON The right mouse button is down.
MK_SHIFT The SHIFT key is down.
MK_XBUTTON1 The XBUTTON1 button is down.
MK_XBUTTON2 The XBUTTON2 button is down.

如果没有对应的标记,则表示没有做对应的动作,例如,测试是否按下 CTRL 键:

if (wParam & MK_CONTROL) { ... }

如果你需要获取其它按键的状态,可以使用 GetKeyState 函数。

WM_XBUTTONDOWN 和 WM_XBUTTONUP 两个窗口消息代表两个扩展按钮的状态。wParam 参数表明具体哪个按钮。

UINT button = GET_XBUTTON_WPARAM(wParam);
if (button == XBUTTON1)
{
    // XBUTTON1 was clicked.
}
else if (button == XBUTTON2)
{
    // XBUTTON2 was clicked.
}

3. 双击

默认情况下窗口不会接收双击消息,如果想要接收双击消息,则需要在注册窗口类填充 WNDCLASS 结构体的时候,设置 CS_DBLCLKS 标记。

WNDCLASS wc = { };
wc.style = CS_DBLCLKS;

/* Set other structure members. */

RegisterClass(&wc);

如果你设置了 CS_DBLCLK 标记,窗口将会接收双击消息。名称中含有 “DBLCLK” 的代表双击事件相关。例如,一个左键双击会产生下面这组事件序列:

WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK WM_LBUTTONUP

实际上会产生两个 WM_LBUTTONDOWN 消息,进而生成 WM_LBUTTONDBLCLK 消息。剩下的按键响应和左键的过程类似。

在确定双击之前,是无法判断第一次单击事件是否是一个双击事件的开始,只有在第二次单击事件发生后,才能判断这两次单击事件是否为一个双击事件。所以双击执行之前会执行单击的附属动作。例如在 Windows 的文件列表窗口,双击一个文件夹,会先执行单击的选中操作,然后执行双击的打开操作。

4. 非客户区鼠标消息

在非客户区的鼠标消息有单独的一套消息名称。这些消息是含有 “NC” 字母名称的消息。例如,WM_NCLBUTTONDOWN 代表非客户区的 WM_LBUTTONDOWN 消息。一般的应用程序是不会拦截这些消息的,因为 DefWindowProc 函数 会默认处理这些消息。然而你通过拦截这些消息可以制作一些特殊的功能。例如,使用这些消息响应制作标题栏的自定义功能。如果不是特别必要请不要拦截这些消息,因为处理这些消息可能会影响默认的窗口行为,如拖拽、最小化等等。

求关注