Win32 多屏时,如何正确获取鼠标位置
事件的由来
当鼠标在窗口内移动时,会收到WM_MOUSEMOVE
,而如果鼠标在窗口外移动,就会收到 WM_NCMOUSEMOVE
事件。NC(Not ClientRect)表示非客户区域的事件。
来看一下消息函数的原型
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
其中 LPARAM
的定义如下,是一个带符号的64位整型
typedef __int64 LONG_PTR, *PLONG_PTR;
typedef LONG_PTR LPARAM;
数据类型
__int64与long long 都是在32位平台开始使用的64位整数的数据类型,在存储方式和使用方式上没有区别。两者的区别在于,它们命名的发起人不同,支持的平台不同。
long long这个数据类型,是UNIX平台发起并支持的,而__int64是微软从win95(VC6)开始发起并支持的,在老的windows开发平台下(如VC6)不识别long long,而老的UNIX,也不识别_int64。当然,现在比较新的平台,两种数据格式和相关的定义、函数都可以兼容了。
作为64位整数的printf输出格式定义,也是一样,微软使用的是%i64d,而UNIX使用的是%lld以及%llu(无符号64位)等形式。
实际上,无论哪一种,在实际效果上没有不同,只是因为定义者和使用环境造成的支持或不支持的问题
注意:ACM中,编译识别系统偏向更多的支持微软系统的定义,因此应该使用__int64和%i64d
变量定义 | 输出方式 | gcc(mingw) | g++(mingw32) | gcc(linux i386) | g++(linux i386) | MicrosoftVisual C++ 6.0 |
---|---|---|---|---|---|---|
long long | %lld | 错误 | 错误 | 正确 | 正确 | 无法编译 |
long long | %I64d | 正确 | 正确 | 错误 | 错误 | 无法编译 |
long long | cout | 非C++ | 正确 | 非C++ | 正确 | 无法编译 |
long long | printint64() | 正确 | 正确 | 正确 | 无法编译 | |
__int64 | lld | 错误 | 错误 | 无法编译 | 无法编译 | 错误 |
__int64 | %I64d | 正确 | 正确 | 无法编译 | 无法编译 | 正确 |
__int64 | cout | 非C++ | 正确 | 非C++ | 无法编译 | 无法编译 |
因此,long = DWORD = __int64,都是表示64bit的数字(只不过DWORD是无符号的)
获取鼠标位置
虽然lParam从定义上看是一个64bit数,但是为了兼容win32,在WM_MOUSEMOVE
和 WM_NCMOUSEMOVE
事件中,它是被当作32位数来处理的
- 低16位表示相对于客户区左上角的 X 坐标
- 高16位表示相对于客户区左上角的 Y 坐标
我们经常用LOWORD
与HIWORD
宏来获取32位值的低位与高低值,宏定义如下
#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
使用方法为
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
但这里不能这么使用。因为在多屏时,窗口会跑到其它屏幕中, 这时鼠标位置可能会小于0, 这时你还在用LOWORD宏就悲剧了。因为此时,编译器把LOWORD()获取出的值当做无符号值来处理了(实际的xPos值可能为负数)
正确代码如下
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
此宏在windowsx.h
或atlwin.h
头文件中有声明
你也可以使用 MAKEPOINTS
宏指令把 lParam 转化成 POINTS 结构
POINTS pt = MAKEPOINTS(lParam);
printf("point x=%d, y=%d\n", pt.x, pt.y);
POINTS 结构体为
typedef struct tagPOINTS
{
#ifndef _MAC
SHORT x;
SHORT y;
#else
SHORT y;
SHORT x;
#endif
} POINTS, *PPOINTS, *LPPOINTS;
- 点赞
- 收藏
- 关注作者
评论(0)