Неправильная точка доступа для курсора I-Beam в Windows 7?
Эта проблема
В окнах координаты, возвращаемые для события "кнопка мыши вниз", кажутся немного неправильными для курсора I-Beam. По сути, координата x всегда находится в двух пикселях от того места, где она должна быть.
Я написал очень простую программу для Win32, чтобы продемонстрировать проблему. Все, что он делает, это превращает курсор в IBeam и визуализирует вертикальную красную линию, где было последнее событие нажатия мыши. Я ожидаю, что красная линия точно совпадет с вертикальной частью двутавровой балки, но это не так.
Вот скриншот того, что происходит.
Как вы можете видеть, красная линия находится в двух пикселях слева от того места, где она должна быть (поведение корректно для стандартного указателя стрелки), поэтому кажется, что точка доступа для курсора I-Beam неверна.
У меня был кто-то еще, работающий под управлением Windows 7 64-битный, подтвердите, что они сталкиваются с той же проблемой, но другой тестер на Vista не имеет проблемы.
Некоторая информация о моей среде
- Windows 7 64 бит. Конфигурация полностью по умолчанию (т.е. нет масштабирования DPI, нет странных тем и т. Д.)
- Visual Studio Express 2010
- Видеокарта NVidia с последними драйверами (v270.61)
- Включение или выключение aero не имеет значения. Выбор разных курсоров в настройках дисплея не имеет значения
Соответствующие биты кода
Мой тестовый проект - это в основном шаблон Win32 Project в Visual C++ 2010 с изменениями, описанными ниже.
Вот код, где я регистрирую класс окна и устанавливаю курсор на 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
Структура для курсора указывает, что координата х для горячей точки курсора находится в точке х =8. Однако, когда я сбрасываю растровое изображение для курсора (hbmMask
член ICONINFO
struct, так как это монохромный курсор), вертикальная полоса находится в 10 пикселях слева от изображения, а не в 8 пикселях. Как указывает Марк, это, вероятно, является причиной визуальной несоответствия, но почему это произошло и как я могу это исправить?
(Я также заметил, что ответ на этот другой вопрос содержит некоторую интересную информацию о том, как обрабатываются курсоры I-Beam. Интересно, уместно ли это)
0 ответов
Меня это мучило много лет, и, видимо, многих других пользователей Windows? Вы когда-нибудь просто щелкаете между двумя символами, но текстовая вставка оказывается слишком далеко влево? Ваш курсор явно находился между двумя другими!
Если вы откроете Блокнот, а затем переместите курсор к нижнему краю, за пределы текстовой области, и увидите изменение между двутавровым лучом и указателем, вы увидите, что указатель начинается на два пикселя слева от двутаврового луча. OP тщательно наблюдал за этим, даже написав программу, чтобы проверить, где именно щелкает этот неправильно работающий двутавровый курсор. Возможно, точка доступа установлена неправильно. (Никогда не думал, что буду одним из тех людей, которые делают снимки экрана на свой телефон, но в данном случае это был самый простой метод, который я придумал для захвата курсора мыши.)
Хорошо, так как нам это исправить? Что ж, любой здравомыслящий пользователь Windows откроет свои настройки мыши на панели управления, а затем очень просто изменит курсор двутаврового луча на другую версию, которая имеет правильную точку доступа. Я мог бы поклясться, что делал это раньше, скачав откуда- то исправленный двутавровый курсор (который выглядел точно так же), но я не могу найти ссылку, откуда я его взял - но да, такой подход наверняка даст вам правильную точку доступа для выделения текста.
Но действительно ли это решит проблему? Или это оставит вас без сна, гадая - зная - вы это прикрыли...
Во всяком случае, это не кажется таким уж сложным... правда? Мы просто пойдем и настроим исходный файл курсора. Итак, я открываю Настройки мыши в Панели управления, а затем нажимаю кнопку Обзор..., ищу в списке вWindows/Cursors
, но... его там нет? Я посмотрел дважды, а потом трижды - точно пропало. Не было ничего похожего на то, что я использовал.
Итак, я заглядываю в реестр Windows через regedit - я решил, что могу найти там путь к файлу. Найти ключи через 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
и, конечно же, нашел ресурсы курсора по умолчанию. Была балка двутавровая под ИД ресурса 73.
Я экспортировал его и посмотрел с помощью шестнадцатеричного редактора, ссылаясь на формат файла ICO. Смещение байта 10 имеет горизонтальную пиксельную координату точки доступа, которая была 8. Я мог бы просто изменить этот байт с0x08
к 0x0A
а затем импортируйте измененный файл обратно в user32.dll с помощью Resource Hacker, и моя проблема будет решена (не считая проблем с разрешениями).
Это достаточно просто, но действительно ли мы хотим простого? Мы зашли так далеко, так что можем зря потратить остаток дня. Напишем для этого программу на C++! Конечно, будучи хорошими инженерами, мы должны найти способ сделать это безопасно и правильно...
Так началось мое путешествие в самые глубокие глубины ада программистов, страдающего от старых документов, описывающих неясные методы WinAPI, например, касающиеся обновления ресурсов DLL. Первым большим препятствием здесь было то магическое число, идентификатор ресурса курсора, который мы хотим изменить, "73". Что это значит? Откуда это?
Что ж, для меня очевидно, что это сгенерированный идентификатор, и ему нельзя доверять как фактическую константу, которая представляет курсор двутавровой балки. Итак, нам нужно найти способ надежно найти это магическое число. На бумаге все выглядит просто, не так ли? Что ж, это не так.
Ближе всего к тому, что я смог найти неуловимое магическое число, была строка "USER32", идентифицирующая модуль, из GetIconInfoEx
. Ничего действительно полезного. (Да, и, кстати, справедливое предупреждение всем, кто хочет выяснить.cur файлы и их разделенный формат BMP.) Если вы найдете способ превратитьIDC_IBEAM
в ключевое имя в ресурсах user32.dll, снимаю шляпу перед вами, но после того, как я ударился головой о стену в большей части этого проекта, я решил пойти с более тупым подходом.
Я просто скопировал исходные данные, экспортировав их прямо из ресурса курсора для использования в качестве подписи. Я мог тогдаLoadLibrary
user32.dll, выполните перечисление с помощью курсоров, а затем проверьте, точно ли они соответствуют файлу подписи. Если я найду совпадение, я нашел идентификатор, который хочу изменить. Я также узнал, что вторым магическим числом, которое я видел в Resource Hacker, был код языка - "1033" (английский, США). Мне пришлось досадно провести еще одно перечисление, чтобы найти это число.
Все хорошо, несколько часов спустя, покопавшись в документации, я нашел свое решение. В Windows API есть функции для обновления ресурсов в файлах DLL. Все, что мне нужно было сделать, это изменить первый байт файла подписи (который являлся горизонтальным смещением горячей точки) и обновить ресурс.
С разрешения конечно
Примерно на середине этого проекта я напомнил себе, что это ужасная идея изменять системные файлы (особенно если это заставляет систему думать, что что-то не так / устарело), не говоря уже о том, какие меры система предпримет, чтобы попытаться остановить вы от этого, но я просто не мог жить без сладкого удовлетворения, зная, что я завершил решение. И это сработало - я сделал копию user32.dll, запустил код, и, конечно же, точка доступа курсора была исправлена.
РЕЗУЛЬТАТ: https://github.com/mukunda-/IBeamFix
Системные файлы - это системные файлы, и даже с доступом администратора система не позволит вам связываться с ними. Я не буду беспокоиться о том, как это обойти.
Возможно, лучшим подходом было бы просто (я имею в виду иметь дело с адом CUR/BMP) экспорт курсора из user32.dll, проверка его характеристик, чтобы убедиться, что он все еще имеет дефект горячей точки, изменение координаты точки доступа и затем обновите реестр, чтобы использовать этот курсор.
Или, что еще лучше, даже не беспокоиться об этом полнейшем безумии и просто использовать замещающий курсор. Мне следовало остановиться на четвертом абзаце. Слушай, я сделал один. https://mukunda.com/stuff/IBeamFixed.cur Проблема решена.