Windows消息参数如何传递与解析?

99ANYc3cd6
预计阅读时长 25 分钟
位置: 首页 参数 正文

这是一个非常核心且重要的概念,理解它对于进行 Windows 桌面程序开发(无论是使用 C++ Win32 API 还是 C#/.NET WinForms/WPF)都至关重要。

windows 消息参数详解
(图片来源网络,侵删)

核心概念:WPARAMLPARAM

在 Windows 消息机制中,每个消息都由一个消息标识符(如 WM_LBUTTONDOWN, WM_PAINT)和两个32位(在64位系统上是64位)的参数组成,这两个参数传统上被称为:

  • WPARAM (Word Parameter):通常用于传递与消息相关的句柄标识符简单的值,在 16 位 Windows 中,它是一个 "word"(16位),现在它被定义为 UINT_PTR(一个无符号整型,其大小足以存储指针)。
  • LPARAM (Long Parameter):通常用于传递指向复杂的数据结构(如矩形、点坐标)或内存地址的指针,在 16 位 Windows 中,它是一个 "long"(32位),现在它被定义为 LONG_PTR(一个长整型,其大小足以存储指针)。

关键点:

  1. 大小:在 64 位系统上,WPARAMLPARAM 都是 64 位的,这允许它们存储更大的值或更复杂的指针。
  2. 含义WPARAMLPARAM具体含义完全取决于消息本身,没有统一的标准,必须查阅微软官方文档来确定某个特定消息的 WPARAMLPARAM 分别代表什么。
  3. 命名W 代表 "Word"(字),L 代表 "Long"(长),这是历史遗留的命名,现在理解它们为“参数1”和“参数2”即可。

消息参数的通用模式

虽然每个消息的参数含义都不同,但我们可以总结出一些常见的模式,这能帮助你快速理解大多数消息。

鼠标消息

鼠标消息是理解参数的绝佳起点。

windows 消息参数详解
(图片来源网络,侵删)
  • WM_LBUTTONDOWN (鼠标左键按下)

    • WPARAM: 包含一个标志位集合,通过位掩码来获取,最常用的是 MK_LBUTTON(左键按下)、MK_RBUTTON(右键按下)、MK_SHIFT(Shift键按下)等。
      // 示例:检查左键是否按下
      if (wParam & MK_LBUTTON) {
          // 左键确实按下了
      }
    • LPARAM: 包含鼠标光标在客户区的坐标,这是一个 LPARAM 类型的值,但实际上是一个打包了两个 short 整数的 32 位值,你可以使用宏 LOWORD()HIWORD() 来分别提取 X 坐标和 Y 坐标。
      short x = LOWORD(lParam); // 获取 X 坐标
      short y = HIWORD(lParam); // 获取 Y 坐标
  • WM_MOUSEMOVE (鼠标移动)

    • WPARAM: 与 WM_LBUTTONDOWN 一样,包含一个标志位集合,表示哪些鼠标键和修饰键被按下。
    • LPARAM: 同样,包含鼠标光标在客户区的坐标。

键盘消息

  • WM_KEYDOWN (非系统键按下)

    • WPARAM: 被按下的虚拟键码VK_A, VK_RETURN, VK_SPACE,这是一个整数值,代表具体的按键。
    • LPARAM: 包含与按键相关的附加信息,同样是一个打包值,你可以使用以下宏来解析:
      • LOWORD(lParam): 重复计数,表示按键被按下的次数(通常为1)。
      • HIWORD(lParam): 扫描码扩展键标志上下文码等。HIWORD(lParam) & KF_EXTENDED 可以判断是否是扩展键(如右 Ctrl 键)。
  • WM_CHAR (字符键按下)

    windows 消息参数详解
    (图片来源网络,侵删)
    • WPARAM: 被按下的字符的 ASCII 码Unicode 码,这是经过转换后的字符,'A' 的 ASCII 码是 65。
    • LPARAM: 与 WM_KEYDOWNLPARAM 含义相同。

绘图消息

  • WM_PAINT (窗口需要重绘)
    • WPARAM: 未使用,始终为 0。
    • LPARAM: 包含一个指向 PAINTSTRUCT 结构体的指针PAINTSTRUCT 结构体包含了绘制所需的所有信息,最重要的成员是 hdc(设备上下文句柄),你需要在上面进行绘制操作。
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps); // 这个函数内部会填充 ps 结构体
      // ... 在 hdc 上进行绘制 ...
      EndPaint(hwnd, &ps);

命令消息

  • WM_COMMAND
    • 这是菜单项、工具栏按钮、加速键等控件发送的通用消息。
    • WPARAM: 一个打包值,通过宏解析:
      • LOWORD(wParam): 控件ID命令ID,你为菜单项定义的 ID_FILE_OPEN
      • HIWORD(wParam): 通知码,对于菜单项,这个值通常是 0,对于其他控件(如列表框),这个值会告诉父控件发生了什么具体事件(如 LBN_SELCHANGE)。
    • LPARAM: 发送命令的控件的句柄,对于菜单和加速键,这个值为 0。

高级主题与 64 位注意事项

WPARAMLPARAM 的类型定义

为了代码的可移植性(32位/64位),微软提供了以下宏来定义消息处理函数的参数类型:

  • WPARAM: 定义为 UINT_PTR
  • LPARAM: 定义为 LONG_PTR
  • LRESULT (消息处理函数的返回值): 定义为 LONG_PTR

这些宏能确保在 32 位系统上它们是 32 位,在 64 位系统上是 64 位。

WPARAMLPARAM 的转换

当你需要将一个指针(如 HWND, LPCTSTR)作为参数传递时,必须进行类型转换。

  • 在 32 位系统中:指针是 32 位的,可以直接赋值给 LPARAMWPARAM
  • 在 64 位系统中:指针是 64 位的,而 LPARAM/WPARAM 也是 64 位的,所以可以直接赋值。

有一个极其重要的转换宏:MAKELPARAM, MAKELONG, MAKEWPARAM

这些宏的作用是将两个 16 位的值(short)打包成一个 32 位的 LPARAMWPARAM,它们在 32 位和 64 位系统上都能正确工作。

为什么不能直接用 (LPARAM)(y << 16 | x) 因为直接位运算在 64 位系统上可能会有符号问题或截断问题,使用宏是唯一安全、可移植的方式。

// 错误的方式(不推荐,可能引发警告或错误)
// LPARAM lParam = (y << 16) | x;
// 正确的方式(推荐)
LPARAM lParam = MAKELPARAM(x, y);
// 解析时
short x = LOWORD(lParam);
short y = HIWORD(lParam);

指针 vs. 值

一个常见的误区是:WPARAM 总是传值,LPARAM 总是传指针。

这是错误的! 两者都可以传值或指针。

  • WM_SETTEXT:
    • WPARAM: 0 (未使用)。
    • LPARAM: 指针,指向一个以 null 结尾的字符串 (LPCTSTR)。
  • WM_SETFONT:
    • WPARAM: ,字体句柄 (HFONT)。
    • LPARAM: ,一个布尔标志,表示是否要立即重绘。

必须查阅文档,不要凭空猜测。


如何查找消息参数的含义?

这是开发者必须掌握的技能。

  1. MSDN (Microsoft Developer Network) 文档:这是最权威、最准确的来源。

    • 在搜索引擎中搜索 "WM_LBUTTONDOWN site:docs.microsoft.com"。
    • 进入该消息的文档页面,通常会有一个 "Parameters" 部分,明确说明了 wParamlParam 的含义。
  2. Visual Studio 帮助

    • 在代码中输入 WM_LBUTTONDOWN,然后按 F1 键,通常会直接跳转到对应的 MSDN 页面。
  3. 头文件

    • 在 Windows SDK 的头文件(如 winuser.h)中,你会找到消息的定义,有时注释中会包含参数说明,但不如 MSDN 详细。

示例:WM_LBUTTONDOWN 的 MSDN 文档片段

wParam

The key flags. This parameter can be a combination of the following values:

  • MK_CONTROL (0x0008): The CTRL key is down.
  • MK_LBUTTON (0x0001): The left mouse button is down.
  • MK_MBUTTON (0x0010): The middle mouse button is down.
  • MK_RBUTTON (0x0002): The right mouse button is down.
  • MK_SHIFT (0x0004): The SHIFT key is down.
  • MK_XBUTTON1 (0x0020): The first X button is down.
  • MK_XBUTTON2 (0x0040): The second X button is down.

lParam

The x-coordinate of the cursor, in client coordinates. The low-order word contains the x-coordinate, and the high-order word contains the y-coordinate.


示例代码:处理鼠标点击消息

下面是一个简单的 Win32 程序片段,展示如何在窗口过程函数中处理 WM_LBUTTONDOWN 消息并解析其参数。

#include <windows.h>
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // ... (窗口注册、创建等代码) ...
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_LBUTTONDOWN: {
            // 解析 WPARAM:获取鼠标按键状态
            if (wParam & MK_CONTROL) {
                OutputDebugString TEXT("Ctrl + ");
            }
            if (wParam & MK_SHIFT) {
                OutputDebugString TEXT("Shift + ");
            }
            OutputDebugString TEXT("Left Mouse Button Clicked.\n");
            // 解析 LPARAM:获取鼠标点击坐标
            short x = LOWORD(lParam); // 获取 x 坐标
            short y = HIWORD(lParam); // 获取 y 坐标
            wchar_t buffer[100];
            swprintf_s(buffer, L"Clicked at (Client): X=%d, Y=%d\n", x, y);
            OutputDebugString(buffer);
            // 在点击位置画一个红点
            HDC hdc = GetDC(hwnd);
            HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
            HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
            MoveToEx(hdc, x, y, NULL);
            LineTo(hdc, x + 1, y + 1); // 画一个 1x1 的点
            SelectObject(hdc, hOldPen);
            DeleteObject(hPen);
            ReleaseDC(hwnd, hdc);
            return 0; // 消息已处理,返回 0
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        // ... 其他消息处理 ...
    }
    // 对于我们没有处理的消息,交给系统默认处理
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
参数 传统含义 现代定义 典型用途
WPARAM Word Parameter (16位) UINT_PTR 句柄、ID、标志位、简单值、指针
LPARAM Long Parameter (32位) LONG_PTR 复杂数据结构指针、坐标、附加信息、指针

核心要点:

  1. 没有固定规则WPARAMLPARAM 的含义取决于具体的消息。
  2. 查阅文档是王道:使用 MSDN 或 VS F1 来确认参数含义。
  3. 使用宏进行打包/解包:对于坐标等 16 位值组合,务必使用 MAKELPARAM/LOWORD/HIWORD
  4. 理解指针与值:两者都可以传递指针或值,不要被名字误导。
  5. 64位兼容性WPARAMLPARAM 在64位系统上是64位的,能安全地存储指针。

掌握 Windows 消息参数是通往精通 Windows 编程的必经之路,希望这份详解对你有帮助!

-- 展开阅读全文 --
头像
美国智能家居品牌哪家强?
« 上一篇 今天
2025款Mac mini内部结构有哪些升级?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]