问题
在窗口上,对于I-Beam光标,“鼠标按钮向下”事件返回的坐标似乎有点错误。基本上,x坐标总是在它应该的位置左边两个像素。
我写了一个非常简单的win32程序来演示这个问题。它所做的只是将光标变成IBeam并渲染一条垂直的红线,其中最后一个鼠标按下事件。我希望红线与I-Beam的垂直部分完全匹配,但事实并非如此。
Here's a screenshot of what happens。
如您所见,红线位于左侧两个像素处(标准箭头指针的行为是正确的),因此I-Beam光标的热点似乎是错误的。 / p>
我让其他人运行Windows 7 64位确认他们遇到了同样的问题,但Vista上的另一个测试人员没有问题。
有关我的环境的一些信息
相关的代码位
我的测试项目基本上是Visual C ++ 2010中的“Win32项目”模板,其中的更改如下所示。
这是我注册窗口类并将光标设置为I Beam
的代码ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CURSOR_TEST));
wcex.hCursor = LoadCursor(NULL, IDC_IBEAM); // this is the only line I changed in this function
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CURSOR_TEST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
以下是我的主要消息循环中的相关部分:
case WM_LBUTTONDOWN:
// record position of mouse down.
// xPos and yPos are just declared as
// global ints for the purpose of this test
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
// cause redraw
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;
case WM_PAINT:
// paint vertical red line at position of last click
hdc = BeginPaint(hWnd, &ps);
RECT rcClient;
GetClientRect(hWnd, &rcClient);
hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
SelectObject(hdc, hPen);
MoveToEx(hdc, xPos, 0, NULL);
LineTo(hdc, xPos, rcClient.bottom);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
break;
摘要
我已经完成了大量的谷歌搜索,但找不到任何相关的东西。我处理传入光标坐标的方式有问题吗?
谢谢!
编辑:评论中有洞察力的问题之后的更多信息
在评论中由@Mark Ransom指导,我使用了GetIconInfo
函数来获取有关I-Beam光标的更多信息。光标的ICONINFO
结构表示光标热点的x坐标位于x = 8。但是,当我转储光标的位图(hbmMask
结构的ICONINFO
成员,因为它是单色光标)时,垂直条距离图像左边10像素,而不是8像素。正如马克指出这可能是视觉差异的原因,但为什么会发生这种情况,我该如何解决呢?
(我也注意到this other question的答案有一些关于I-Beam游标处理的不同方式的有趣信息。我想知道这是否相关)
答案 0 :(得分:0)
这困扰了我很多年,显然还有许多其他Windows用户?您是否曾经单击过两个字符之间,但是文本插入符号的结尾距离左侧太远了?您的光标显然在其他两个光标之间!
如果打开记事本,然后将光标移到底部边缘,移出文本区域,并观察I型束和指针之间的变化,您可以看到指针从I形左边两个像素开始-光束。 OP甚至通过编写程序来测试行为异常的I型光标正在单击的位置,从而彻底观察到了这一点。一定是热点设置不正确。 (从来没有想过我会成为用手机拍摄屏幕截图的人之一,但是在这种情况下,这实际上是我想到的捕获鼠标光标的最简单方法。)
好的,那么我们如何解决呢? 好吧,任何精明的Windows用户都可以在“控制面板”中打开其“鼠标”设置,然后只需将I型光标更改为具有正确热点的其他版本即可。我已经发誓我以前已经做过这件事,已经从 somewhere 下载了一个更正的I型光标(看上去几乎一样),但是我似乎找不到我所在的链接是从中得到的-但是,是的,这种方法肯定会为您提供正确的文本选择热点。
可以真正解决问题吗?还是会让您失眠,想知道-知道-您把它掩盖了...
看起来如此似乎很难真正解决,对吧?我们将去调整原始光标文件。因此,我在“控制面板”中打开“鼠标设置”,然后单击浏览... ,在Windows/Cursors
中搜索列表,但是...不存在吗?我看了两次,然后三次–肯定不见了。没有与我使用的类似的东西。
因此,我通过 regedit 进入Windows注册表–我发现可以在其中找到指向它的文件路径。通过Google查找密钥在哪里很简单:HKEY_CURRENT_USER/Control Panel/Cursors
。但是等等-它也不在那里吗?我看到箭头,手形和其他光标,但在任何地方都没有“ Ibeam”或“ TextSelection”项!
聪明的人可能在嘲笑我的困惑,他们完全知道Windows保留其秘密光标的位置,但是,我的无知使我感到痛苦。我继续不遗余力地寻找其他按键来寻找它-也许文本选择很特殊,并且光标信息在相关按键下的其他位置?
很快我就得出一个合理的假设:如果未设置键,Windows将使用默认的光标文件-但是那个在哪里?幸运的是,我有少量Windows编程经验,并且知道事情可能来自嵌入式资源,而不是独立的.cur文件。经过更深入的挖掘,一个叫赫比的人为我找到了答案:
它们位于user32.dll [%WinDir%/ system32]中。
(通过https://www.neowin.net/forum/topic/374461-default-xp-cursor-location/)
隧道尽头的光。出于某种原因,我已经在计算机上安装了Resource Hacker,这可能是因为我之前做过一些杂乱无章的事情,但是我在user32.dll
内浏览了一下,当然,找到了默认的光标资源。资源ID为73的工字梁。
我导出了它,并在参考ICO文件格式的同时使用了十六进制编辑器。字节偏移量10的热点水平像素坐标为8。我可以将该字节从0x08
更改为0x0A
,然后使用Resource Hacker将修改后的文件导入回user32.dll中,并且我的问题将得到解决(除了许可问题)。
这很简单,但是我们真的想要简单吗?我们已经走了这么远,所以最好还是浪费我们剩下的时间。让我们编写一个C ++程序来做到这一点!当然,作为我们自己是完全好的工程师,我们必须找到一种方法来做到这一点的安全和适当……
因此,我开始了进入程序员地狱最深处的旅程,遭受了以往文档的描述,这些文档描述了模糊的WinAPI方法,例如那些与更新DLL资源有关的方法。这里的第一个主要障碍是魔术数字,即我们要修改的光标资源的ID,为“ 73”。什么意思它是从哪里来的?
好吧,对我来说,很明显,这是一个生成的ID,因此不能相信它是代表工字梁光标的实际常量。因此,我们需要找到某种方法来可靠地找到该魔数。在纸上看起来很简单,不是吗?好吧,不是。
最近我要找出难以捉摸的魔术数字是来自GetIconInfoEx
的字符串“ USER32”,用于标识模块。实际上没有任何用处。 (哦,顺便说一句,对任何想弄清楚.cur文件及其保留的BMP格式的人都应该给予警告。)如果您可以找到一种方法将IDC_IBEAM
转换为user32.dll资源中的键名, ,向您致以崇高的敬意,但是在我为这个项目的大部分时间把头撞在墙上之后,我决定采用愚蠢的方法。
我只是复制了原始数据,直接从游标资源中将其导出以用作签名。然后,我可以LoadLibrary
user32.dll,通过游标枚举,然后检查它们是否与签名文件完全匹配。如果找到匹配项,则找到了要修改的ID。我还了解到,我在Resource Hacker中看到的第二个魔术数字是语言代码–“ 1033”(美国英语)。我也不得不烦恼地进行另一个枚举才能找到该数字。
一切顺利,从翻阅文档几小时后,我有了解决方案。 Windows API中有一些功能可以更新DLL文件中的资源。我要做的就是更改签名文件的第一个字节(这是水平热点偏移),然后更新资源。
在这个项目进行到大约一半时,我提醒自己,修改系统文件是一个可怕的主意(尤其是如果它使系统认为有问题/过时),更不用说系统将采取什么措施了。尝试阻止您这样做,但是,如果没有甜蜜的满足感就知道我完成了解决方案,那么我就无法生存。它确实起作用了–我制作了user32.dll的副本,运行了代码,并且确定光标热点已得到纠正。
结果:https://github.com/mukunda-/IBeamFix
系统文件不过是系统文件,即使没有管理员访问权限,系统也不会让您弄乱它们。我不会费心找出解决方法。
一种更好的方法可能是简单地(并且简单地说,我是指与CUR / BMP地狱打交道)从user32.dll中导出光标,检查其特征以确保它仍然具有热点缺陷,并修改了热点进行协调,然后更新注册表以使用该光标。
或者,甚至更好的是,甚至不必为任何这种疯狂而烦恼,而只是使用替换光标。我应该在第四段停下来。看,我做了一个。 https://mukunda.com/stuff/IBeamFixed.cur问题已解决。