Настройка осведомленности о DPI процесса, чтобы система компенсировала коэффициенты масштабирования.

У меня есть следующая процедура крючка мыши (упрощенная для объяснения).

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-осведомленными и... просто так... они снова будут работать.


person Seth    schedule 21.06.2020    source источник
comment
Низкоуровневый хук мыши запускается еще до того, как система начала определять целевое окно. Следовательно, система не может применять какую-либо виртуализацию к координатам.   -  person IInspectable    schedule 21.06.2020


Ответы (1)


Я не понимаю, почему Microsoft решила сделать это наоборот. Программы, которые не поддерживают DPI, должны вести себя как обычно, а не наоборот.

Фактически, документ Microsoft объясняет эту проблему.

Из Настольный компьютер с высоким разрешением Разработка приложений для Windows,

Настольные приложения, использующие более старые технологии программирования Windows (необработанное программирование Win32, Windows Forms, Windows Presentation Framework (WPF) и т. д.), не могут автоматически обрабатывать масштабирование DPI без дополнительной работы разработчика. Без такой работы приложения будут выглядеть размытыми или иметь неправильный размер во многих распространенных сценариях использования.

Если вы хотите избежать проблем с определением DPI, создайте приложение UWP.

Для начала, если вы создаете новое приложение для Windows с нуля, настоятельно рекомендуется создать приложение для универсальной платформы Windows (UWP). Приложения UWP автоматически и динамически масштабируются для каждого дисплея, на котором они работают.

person Strive Sun    schedule 22.06.2020
comment
Извините, это даже не попытка ответить на вопрос. Очевидно, что вызов SetWindowsHookEx ограничен. только для настольных приложений. Говорить OP о написании приложения UWP, просто чтобы они не сталкивались с проблемами осведомленности о DPI, бесполезно. Вдобавок к упущенному моменту, низкоуровневый хук мыши полностью выходит за рамки бизнеса с поддержкой DPI. И вдобавок ко всему, рекомендовать UWP перед лицом Project Reunion довольно сложно. - person IInspectable; 22.06.2020