wParam 和 lParam 是 Windows 消息处理中的两个重要参数

2024-08-25 05:44

本文主要是介绍wParam 和 lParam 是 Windows 消息处理中的两个重要参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在 Windows API 中,许多消息处理函数都会接收 32 位参数(如 lParamwParam),这些参数用来传递与特定消息相关的附加信息。这些参数通常会包含多个有用的值或标志,因此理解如何解析和使用这些参数对于 Windows 编程非常重要。

什么是 wParamlParam

wParamlParam 是 Windows 消息传递机制中的两个参数,它们用于传递额外的消息信息。

  1. wParamWord Parameter
    • 类型WPARAM,通常是一个无符号整数(UINT_PTR),32 位或 64 位,取决于平台。
    • 用途:通常用于传递消息的辅助信息,较常用于传递标志、标识符或简短的附加数据。
  2. lParamLong Parameter
    • 类型LPARAM,通常是一个有符号长整数(LONG_PTR),32 位或 64 位,取决于平台。
    • 用途:用于传递更复杂的消息信息,如两个 16 位值的组合、指针、坐标信息等。

32 位参数的用法

wParamlParam 是 32 位的通用数据类型,它们的具体内容和格式取决于消息类型。下面是如何使用它们的几个常见示例。

1. 鼠标消息(如 WM_MOUSEMOVEWM_LBUTTONDOWN

在鼠标消息中,lParam 通常包含鼠标的坐标信息。

  • lParam 的低 16 位(通过 LOWORD(lParam) 获取):表示鼠标的 X 坐标。
  • lParam 的高 16 位(通过 HIWORD(lParam) 获取):表示鼠标的 Y 坐标。
case WM_MOUSEMOVE: {int xPos = LOWORD(lParam);  // 提取鼠标的 X 坐标int yPos = HIWORD(lParam);  // 提取鼠标的 Y 坐标// 根据 xPos 和 yPos 进行操作break;
}

在这个例子中,lParam 的 32 位被分成两个 16 位部分:低位部分用于 X 坐标,高位部分用于 Y 坐标。

2. 键盘消息(如 WM_KEYDOWNWM_KEYUP

在键盘消息中,wParam 通常包含虚拟键码,而 lParam 包含按键状态的信息,如重复计数和扫描码。

  • wParam:虚拟键码(例如 VK_RETURN 表示 Enter 键)。
  • lParam 的低 16 位:包含按键按下的重复次数。
  • lParam 的高 16 位:包含键的扫描码和其他状态标志(如扩展键标志、前一状态标志等)。
case WM_KEYDOWN: {int virtualKeyCode = (int) wParam;  // 获取虚拟键码int repeatCount = LOWORD(lParam);   // 获取重复计数int scanCode = (HIWORD(lParam) & 0xFF); // 获取扫描码// 根据 virtualKeyCode、repeatCount 和 scanCode 进行操作break;
}
3. 窗口管理消息(如 WM_SIZE

在窗口管理消息中,lParam 可能包含窗口的新尺寸。

  • LOWORD(lParam):新窗口的宽度。
  • HIWORD(lParam):新窗口的高度。
case WM_SIZE: {int newWidth = LOWORD(lParam);   // 提取窗口的新宽度int newHeight = HIWORD(lParam);  // 提取窗口的新高度// 使用 newWidth 和 newHeight 进行窗口调整break;
}
4. 控件命令消息(如 WM_COMMAND

在命令消息中(例如用户点击按钮或菜单项),wParamlParam 包含了控件的相关信息。

  • LOWORD(wParam):控件的 ID 或命令标识符。
  • HIWORD(wParam):通知代码,指示发生了什么事件(如按钮被点击)。
  • lParam:控件的句柄(HWND)。
case WM_COMMAND: {int wmId = LOWORD(wParam);      // 获取控件 ID 或命令 IDint wmEvent = HIWORD(wParam);   // 获取通知码HWND hwndCtrl = (HWND)lParam;   // 获取控件句柄// 根据 wmId 和 wmEvent 进行操作break;
}

32 位参数的分解方法

对于 Windows 消息中的 32 位参数(如 lParamwParam),通常使用以下宏进行分解:

  • LOWORD(x):获取参数 x 的低 16 位。
  • HIWORD(x):获取参数 x 的高 16 位。
  • LOBYTE(x):获取参数 x 的低 8 位。
  • HIBYTE(x):获取参数 x 的高 8 位。

这些宏通过简单的掩码和位移操作从 32 位数中提取出所需的部分,便于访问其中的各个字段。

总结

wParamlParam 是 Windows 消息处理中的两个重要参数,用于传递与特定消息相关的各种信息。这些参数通常是 32 位的(在 64 位系统上也是 32 位),可以使用宏来提取其中的低 16 位或高 16 位数据,从而得到消息附带的信息,如坐标、按键状态、控件 ID 等。理解如何解析和使用这些参数对于 Windows 编程是至关重要的。

--------------------

理解如何解析和使用 wParamlParam 参数对于 Windows 编程至关重要,因为它们是传递给窗口过程函数的所有消息的核心部分。准确地解析这些参数,可以帮助程序响应用户的输入和系统事件,从而提高程序的交互性和稳定性。以下是更深入的探讨,说明为什么理解和正确使用这些参数如此重要。

为什么解析和使用 wParamlParam 很重要?

  1. 实现精确的用户交互

    • 大多数用户交互(如鼠标点击、键盘输入)和系统事件都通过 wParamlParam 传递信息。例如,当用户在窗口中点击鼠标时,系统生成一个 WM_LBUTTONDOWN 消息,并将鼠标的 x 和 y 坐标存储在 lParam 中。通过解析 lParam,程序可以精确地知道鼠标点击的位置,从而对用户的操作做出正确响应。
  2. 支持多样化的输入和命令

    • 不同的消息类型使用 wParamlParam 传递不同的信息。例如,在 WM_KEYDOWN 消息中,wParam 代表按下的虚拟键码,而在 WM_COMMAND 消息中,wParam 的低位字表示控件的 ID 或命令的标识符,HIWORD(wParam) 代表通知代码。理解这些差异有助于正确地处理不同类型的输入和命令。
  3. 优化消息处理和资源管理

    • 通过正确解析消息参数,程序可以有选择性地处理特定的消息,避免不必要的处理。这种优化对于性能至关重要,尤其是在高频事件(如鼠标移动、键盘输入)或资源管理(如内存、文件句柄分配和释放)场景中。
  4. 确保应用程序的稳定性

    • 如果 wParamlParam 被误解或误用,程序可能会做出错误的行为,导致异常或崩溃。例如,如果 WM_SIZE 消息中窗口的大小参数没有正确解析,窗口布局可能会错误,甚至导致程序崩溃。正确处理这些参数有助于确保应用程序的稳定性和可靠性。
  5. 增强应用程序的可扩展性和维护性

    • 理解消息的含义和参数解析的逻辑,使得程序更加模块化和结构化。这样,可以更容易地修改或扩展功能。例如,通过定义一个清晰的消息处理机制,未来可以轻松地添加新功能,而不需要重构整个代码。

深入解析常见消息参数的例子

1. 鼠标事件的 lParam 解析

对于鼠标事件(如 WM_MOUSEMOVEWM_LBUTTONDOWN),lParam 包含了鼠标的坐标。正确解析这些坐标是处理鼠标事件的关键。

case WM_LBUTTONDOWN: {int xPos = LOWORD(lParam);  // 提取鼠标的 x 坐标(低 16 位)int yPos = HIWORD(lParam);  // 提取鼠标的 y 坐标(高 16 位)// 根据坐标执行相应的操作,例如绘制或选择对象break;
}

在这个例子中,LOWORD(lParam) 提取了鼠标的 X 坐标,而 HIWORD(lParam) 提取了 Y 坐标。通过解析这些坐标,程序能够精确知道鼠标的点击位置,执行相应操作。

2. 键盘事件的 wParamlParam 解析

在键盘事件(如 WM_KEYDOWNWM_KEYUP)中,wParamlParam 提供了丰富的按键信息。通过解析这些参数,程序可以确定用户按下的键和键的状态

case WM_KEYDOWN: {int virtualKeyCode = (int)wParam;  // 获取虚拟键码(如 VK_RETURN)int repeatCount = LOWORD(lParam);  // 获取按键重复计数int scanCode = (HIWORD(lParam) & 0xFF);  // 提取扫描码(高字节的低 8 位)int isExtendedKey = (HIWORD(lParam) & KF_EXTENDED) ? 1 : 0;  // 扩展键标志// 根据虚拟键码和其他状态执行相应的操作break;
}

在这个例子中:

  • wParam 表示按下的键的虚拟键码。
  • lParam 的低 16 位表示按键的重复次数(如果用户长按键)。
  • HIWORD(lParam) 的低 8 位是扫描码,表示物理按键的硬件编码。
  • KF_EXTENDED 是一个标志,表示按键是否为扩展键(如功能键)。
3. 窗口调整的 lParam 解析

在窗口调整大小的事件(如 WM_SIZE)中,lParam 包含了窗口的新尺寸。通过解析这些参数,程序可以动态调整窗口的布局和内容。

case WM_SIZE: {int newWidth = LOWORD(lParam);   // 获取新窗口宽度(低 16 位)int newHeight = HIWORD(lParam);  // 获取新窗口高度(高 16 位)// 根据新尺寸调整窗口内容或布局break;
}

在这个例子中,LOWORD(lParam)HIWORD(lParam) 分别提取窗口的新宽度和高度,帮助程序响应窗口尺寸变化。

实践提示和最佳实践

  1. 使用适当的宏来解析参数

    • 使用 LOWORDHIWORDLOBYTEHIBYTE 宏来从 32 位参数中提取需要的部分,避免手动位操作错误。
  2. 检查消息文档

    • 每种消息的 wParamlParam 用法可能不同,因此在编写代码时参考 微软官方文档,了解每条消息的参数含义和用法。
  3. 处理所有可能的消息

    • 在窗口过程函数中,要确保处理了所有可能接收到的消息,或者使用 DefWindowProc 处理不需要特别处理的消息,以避免意外行为。
  4. 使用调试工具验证行为

    • 在调试期间使用工具(如 Visual Studio 的调试器)来检查消息和参数,确保消息处理逻辑正确无误。

总结

理解如何正确解析和使用 wParamlParam 是 Windows 编程的核心技能之一。这些参数传递了丰富的信息,帮助程序响应用户操作和系统事件。通过正确解析这些参数,开发者可以创建高效、稳定和响应迅速的应用程序。确保在处理消息时精确地解析这些参数是开发高质量 Windows 应用程序的基础。

#include <windows.h>
#include <stdio.h>#define IDC_STATIC_TEXT 1001 // 控件ID// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {static HWND hStatic; // 静态控件句柄switch (msg) {case WM_CREATE: {// 创建静态控件hStatic = CreateWindow("STATIC",          // 控件类名"",                // 控件初始文本WS_CHILD | WS_VISIBLE | SS_LEFT, // 控件样式50, 60, 300, 20,   // 位置和大小hwnd,              // 父窗口句柄(HMENU)IDC_STATIC_TEXT, // 控件ID((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄NULL               // 附加应用程序数据);if (hStatic == NULL) {MessageBox(hwnd, "Failed to create static control!", "Error", MB_OK | MB_ICONERROR);return -1; // 创建失败,退出消息循环}break;}case WM_LBUTTONDOWN: { // 处理左键点击事件int xPos = LOWORD(lParam);  // 获取鼠标x坐标int yPos = HIWORD(lParam);  // 获取鼠标y坐标char buf[100];snprintf(buf, sizeof(buf), "Left button clicked at (%d, %d)", xPos, yPos);SetWindowText(hStatic, buf);  // 更新静态控件文本break;}case WM_RBUTTONDOWN: { // 处理右键点击事件int xPos = LOWORD(lParam);  // 获取鼠标x坐标int yPos = HIWORD(lParam);  // 获取鼠标y坐标char buf[100];snprintf(buf, sizeof(buf), "Right button clicked at (%d, %d)", xPos, yPos);SetWindowText(hStatic, buf);  // 更新静态控件文本break;}case WM_MOUSEMOVE: { // 处理鼠标移动事件int xPos = LOWORD(lParam);  // 获取鼠标x坐标int yPos = HIWORD(lParam);  // 获取鼠标y坐标char buf[100];snprintf(buf, sizeof(buf), "Mouse moved to (%d, %d)", xPos, yPos);SetWindowText(hStatic, buf);  // 更新静态控件文本break;}case WM_DESTROY:PostQuitMessage(0); // 退出消息循环break;default:return DefWindowProc(hwnd, msg, wParam, lParam); // 默认窗口过程}return 0;
}// 程序入口
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {const char CLASS_NAME[] = "Sample Window Class";// 定义窗口类WNDCLASS wc = {};wc.lpfnWndProc = WndProc;          // 窗口过程函数wc.hInstance = hInstance;          // 实例句柄wc.lpszClassName = CLASS_NAME;     // 窗口类名wc.hCursor = LoadCursor(NULL, IDC_ARROW);  // 设置光标// 注册窗口类if (!RegisterClass(&wc)) {MessageBox(NULL, "Window Class Registration Failed!", "Error", MB_OK | MB_ICONERROR);return 0;}// 创建窗口HWND hwnd = CreateWindowEx(0,                              // 可选窗口样式CLASS_NAME,                     // 窗口类名"Sample Window",                // 窗口标题WS_OVERLAPPEDWINDOW,            // 窗口样式CW_USEDEFAULT, CW_USEDEFAULT,   // 初始位置CW_USEDEFAULT, CW_USEDEFAULT,   // 初始尺寸NULL,                           // 父窗口NULL,                           // 菜单hInstance,                      // 实例句柄NULL                            // 附加应用程序数据);if (hwnd == NULL) {MessageBox(NULL, "Window Creation Failed!", "Error", MB_OK | MB_ICONERROR);return 0;}// 显示窗口ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);// 消息循环MSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}
消息循环是 Windows 应用程序的核心部分,它确保应用程序能够响应用户输入和其他事件。
通过 GetMessage 函数获取消息,
TranslateMessage 函数翻译键盘消息,
DispatchMessage 函数分派消息到窗口过程函数,消息循环使得程序能够处理用户交互和系统事件,保持程序的运行和响应。
#include <windows.h>
#include <stdio.h>LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {switch (msg) {case WM_KEYDOWN: {// 获取虚拟键码int virtualKeyCode = (int)wParam;// 获取按键重复计数int repeatCount = LOWORD(lParam);// 提取扫描码(高字节的低 8 位)int scanCode = (HIWORD(lParam) & 0xFF);// 扩展键标志int isExtendedKey = (HIWORD(lParam) & KF_EXTENDED) ? 1 : 0;// 格式化信息并更新窗口标题栏char buf[256];snprintf(buf, sizeof(buf), "VK Code: %d, Repeat Count: %d, Scan Code: %d, Extended Key: %d",virtualKeyCode, repeatCount, scanCode, isExtendedKey);SetWindowText(hwnd, buf);// 输出到调试控制台printf("VK Code: %d, Repeat Count: %d, Scan Code: %d, Extended Key: %d\n",virtualKeyCode, repeatCount, scanCode, isExtendedKey);break;}case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd, msg, wParam, lParam);}return 0;
}int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {const char CLASS_NAME[] = "Sample Window Class";// 定义窗口类WNDCLASS wc = {};wc.lpfnWndProc = WndProc;wc.hInstance = hInstance;wc.lpszClassName = CLASS_NAME;wc.hCursor = LoadCursor(NULL, IDC_ARROW);  // 添加光标// 注册窗口类if (!RegisterClass(&wc)) {MessageBox(NULL, "Window Class Registration Failed!", "Error", MB_OK | MB_ICONERROR);return 0;}// 创建窗口HWND hwnd = CreateWindowEx(0,CLASS_NAME,"Key Info Window",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL);if (hwnd == NULL) {MessageBox(NULL, "Window Creation Failed!", "Error", MB_OK | MB_ICONERROR);return 0;}// 显示窗口ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);// 消息循环MSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}
// 解释:
// WM_KEYDOWN 消息处理:// 虚拟键码 (virtualKeyCode) 从 wParam 中获取。
// 按键重复计数 (repeatCount) 从 lParam 的低字节中获取。
// 扫描码 (scanCode) 从 lParam 的高字节中提取(通过 HIWORD(lParam) & 0xFF)。
// 扩展键标志 (isExtendedKey) 从 lParam 的高字节中提取(通过 HIWORD(lParam) & KF_EXTENDED)。
// 使用 snprintf 格式化信息并更新窗口标题栏,以显示按键的详细信息。
// 输出到调试控制台:// 使用 printf 将按键信息输出到调试控制台,便于调试和验证信息的正确性。
// 窗口创建和消息循环:// 程序创建一个窗口并进入消息循环,以处理窗口消息,包括按键消息。
// 这个示例演示了如何处理 WM_KEYDOWN 消息并获取相关的按键信息,以及如何将这些信息显示在窗口标题栏上。你可以运行这个程序并按下不同的键,以查看窗口标题栏中显示的按键信息。

 

这篇关于wParam 和 lParam 是 Windows 消息处理中的两个重要参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104710

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而