GetMessagePos 不是标准的 C 语言库函数,而是 Windows API (应用程序编程接口) 中的一个函数,它只在 Windows 操作系统上进行 Windows 开发时才会用到。

它的主要作用是:获取消息队列中上一条消息的鼠标光标屏幕坐标位置。
函数原型
GetMessagePos 的原型定义在 windows.h 头文件中。
DWORD GetMessagePos(void);
- 返回值类型:
DWORD(一个 32 位无符号整数)。 - 参数: 无。
返回值详解
函数返回一个 DWORD 类型的值,它是一个 打包的坐标值,这个值包含了鼠标的 X 坐标和 Y 坐标。
- 低 16 位: 代表鼠标的 X 坐标。
- 高 16 位: 代表鼠标的 Y 坐标。
这种将两个坐标打包成一个数值的方式在 Windows API 中很常见,可以方便地在函数间传递。

如何拆解这个返回值?
你需要使用 Windows API 提供的宏或函数来分离 X 和 Y 坐标。
使用宏 (推荐)
Windows 提供了两个宏 GET_X_LPARAM 和 GET_Y_LPARAM 来方便地提取坐标,注意,虽然函数返回的是 DWORD,但通常我们会把它当作 LPARAM 类型来处理,所以使用这两个宏是最稳妥的。
// 假设 pos 是 GetMessagePos() 的返回值 DWORD pos = GetMessagePos(); int x = GET_X_LPARAM(pos); int y = GET_Y_LPARAM(pos);
使用位运算 (原理)

如果你了解位运算,也可以手动拆解:
DWORD pos = GetMessagePos(); // 将 pos 强制转换为 LPARAM 类型以确保宏正确工作 LPARAM lParam = (LPARAM)pos; int x = (int)(short)LOWORD(lParam); // 获取低16位 int y = (int)(short)HIWORD(lParam); // 获取高16位
注意:
LOWORD和HIWORD也是 Windows 宏,用于获取一个 32 位值的低16位和高16位,将结果强制转换为short再转换为int是为了处理符号位,确保坐标值的正确性。
工作原理与使用场景
GetMessagePos 获取的是消息队列中上一条消息(不一定是当前正在处理的消息)发生时的鼠标位置。
重要特性:
- 它获取的是屏幕坐标,而不是相对于某个窗口的客户区坐标。
- 即使鼠标当前已经移动,只要消息队列里还有一条旧消息,
GetMessagePos返回的就是那条旧消息发生时的位置。
常见使用场景
-
在鼠标消息处理中获取精确位置: 在处理如
WM_LBUTTONDOWN,WM_MOUSEMOVE等消息时,消息本身会附带lParam参数,里面也包含了鼠标坐标,但有时你可能需要获取与当前消息相关的、但位置略有不同的参考点,这时GetMessagePos就派上用场。case WM_LBUTTONDOWN: { // 获取消息本身的坐标 (客户区坐标) int x = LOWORD(lParam); int y = HIWORD(lParam); // 获取上一条消息的屏幕坐标 DWORD lastPos = GetMessagePos(); int screenX = GET_X_LPARAM(lastPos); int screenY = GET_Y_LPARAM(lastPos); // ... 使用这些坐标 ... break; } -
在没有直接鼠标消息的情况下获取鼠标位置: 比如你的程序正在通过
GetMessage或PeekMessage循环等待一个自定义消息,但你希望在处理这个消息时知道用户最后一次点击鼠标的位置在哪里,这时GetMessagePos就非常有用。MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_USER_CUSTOM_COMMAND) { // 用户触发了自定义命令,我们想知道他最后点击在哪 DWORD lastClickPos = GetMessagePos(); int clickX = GET_X_LPARAM(lastClickPos); int clickY = GET_Y_LPARAM(lastClickPos); // ... 根据点击位置执行操作 ... } TranslateMessage(&msg); DispatchMessage(&msg); } -
在非客户区或系统菜单消息中: 当处理如
WM_NCHITTEST(测试鼠标在窗口的哪个非客户区) 或WM_SYSCOMMAND(系统命令) 时,这些消息的lParam提供的是屏幕坐标。GetMessagePos的返回值通常与LOWORD(lParam)和HIWORD(lParam)的值相同,因为它获取的就是触发该消息的那个鼠标事件的坐标。
完整示例代码
下面是一个简单的 Windows 程序,当你在窗口内点击鼠标时,它会在点击位置(屏幕坐标)绘制一个红点。
#include <windows.h>
#include <stdio.h>
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// 窗口类名
const wchar_t* CLASS_NAME = L"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, L"Window Registration Failed!", L"Error", MB_ICONERROR);
return 0;
}
// 创建窗口
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"GetMessagePos Demo", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 在 WM_PAINT 中通常不进行复杂的绘制,这里简单演示
EndPaint(hwnd, &ps);
}
return 0;
case WM_LBUTTONDOWN:
{
// 获取上一条消息的鼠标屏幕坐标
DWORD pos = GetMessagePos();
int x = GET_X_LPARAM(pos);
int y = GET_Y_LPARAM(pos);
// 在控制台打印坐标
wchar_t buffer[100];
swprintf_s(buffer, L"Mouse clicked at screen coordinates: (%d, %d)", x, y);
OutputDebugStringW(buffer);
// 获取设备上下文并绘制一个红点
HDC hdc = GetDC(hwnd);
HPEN hPen = CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
// 将屏幕坐标转换为窗口客户区坐标 (重要步骤!)
POINT pt = { x, y };
ScreenToClient(hwnd, &pt);
// 绘制一个5像素半径的圆点
Ellipse(hdc, pt.x - 5, pt.y - 5, pt.x + 5, pt.y + 5);
// 清理
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
ReleaseDC(hwnd, hdc);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
与相关函数的对比
| 函数名 | 获取的位置坐标 | 坐标系 | 关键区别 |
|---|---|---|---|
GetMessagePos |
消息队列中上一条消息的鼠标位置 | 屏幕坐标 | 基于消息队列,获取的是历史位置。 |
GetCursorPos |
当前鼠标光标的位置 | 屏幕坐标 | 获取的是鼠标此刻的实时位置。 |
LOWORD(lParam) / HIWORD(lParam) |
当前正在处理的鼠标消息的鼠标位置 | 取决于消息类型 | 对于 WM_xBUTTONDOWN 等是客户区坐标,对于 WM_NCHITTEST 等是屏幕坐标。 |
简单总结:
- 想知道鼠标现在在哪?用
GetCursorPos。 - 想知道触发某个消息时鼠标在哪?用消息的
lParam。 - 想知道上一条鼠标消息发生时鼠标在哪?用
GetMessagePos。
希望这个详细的解释能帮助你完全理解 GetMessagePos 的用法!
