У меня есть следующая процедура крючка мыши (упрощенная для объяснения).
SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandle(NULL), 0) ;
LRESULT mouseHookProc(int code, WPARAM wParam, LPARAM lParam){
if(code==HC_ACTION){
const auto& data = *(MSLLHOOKSTRUCT*)lParam ;
data.pt ; //This point gives physical coordinates. It ignores the monitor's scaling factor.
//https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msllhookstruct
}
return CallNextHookEx(NULL, code, wParam, lParam) ;
}
Координаты мыши, которые я получаю от хука, не регулируются коэффициентом масштабирования монитора.
Независимо от того, устанавливаю ли я коэффициент масштабирования моего монитора на 100% или 200%, хук мыши всегда дает мне физические пиксели.
С другой стороны, функция winapi GetCursorPos
дает координаты в логических пикселях. т. е. если коэффициент масштабирования равен 200%, GetCursorPos
даст координаты, деленные на 2, тогда как хук мыши даст нескорректированные числа.
В соответствии с этим:
https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness
если программа НЕ поддерживает DPI, система компенсирует коэффициент масштабирования, чтобы программа продолжала работать, как будто ничего не произошло.
Это именно то, что происходит с возвращаемым значением GetCursorPos
. Это дает логические пиксели, а не физические.
Процедура хука мыши, с другой стороны, не регулируется системой.
Я попытался настроить свою программу как неосведомленную о DPI в манифесте следующим образом:
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">unaware</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
Но это не имеет значения. Я также попытался объявить его DPI-осведомленным следующим образом:
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware>True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
И это тоже не имело значения.
Есть ли какой-либо параметр, который я могу добавить в свою программу, либо через манифест, либо иным образом, который заставит процедуру захвата мыши давать логические координаты, как это делает GetCursorPos
?
Я тестирую все это в Windows 10.
ПОСЛЕДУЮЩИЕ ДЕЙСТВИЯ:
Я нашел проблему. Мой файл манифеста был неправильным. Пространства имен XML были заданы неправильно.
Вот тот, который работает.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="AppName" type="win32"/>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
Чтобы система давала программе логические координаты, программа должна поддерживать DPI. Программы, которые не проявляют DPI-осведомленность, будут обрабатывать координаты непоследовательно во всем, не только в крючке мыши, а также в BitBlt, IAccesibility, UIAutomation и т. д.
Я не понимаю, почему Microsoft решила сделать это наоборот. Программы, которые не поддерживают DPI, должны вести себя как обычно, а не наоборот.
Это означает, что всякий раз, когда на мониторе устанавливается коэффициент масштабирования, большинство программ по умолчанию прерываются. Чтобы разблокировать их, они должны стать DPI-осведомленными и... просто так... они снова будут работать.