Win32 多屏时,如何正确获取鼠标位置

举报
福州司马懿 发表于 2022/11/20 00:41:37 2022/11/20
【摘要】 事件的由来当鼠标在窗口内移动时,会收到WM_MOUSEMOVE,而如果鼠标在窗口外移动,就会收到 WM_NCMOUSEMOVE 事件。NC(Not ClientRect)表示非客户区域的事件。来看一下消息函数的原型LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)其中 LPARAM ...

事件的由来

当鼠标在窗口内移动时,会收到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_MOUSEMOVEWM_NCMOUSEMOVE 事件中,它是被当作32位数来处理的

  • 低16位表示相对于客户区左上角的 X 坐标
  • 高16位表示相对于客户区左上角的 Y 坐标

我们经常用LOWORDHIWORD宏来获取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.hatlwin.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;
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。