Mixed-Mode API масштабирования на дюйм и api с поддержкой DPI

Поддержка осведомленности по Sub-Process DPI

SetThreadDpiAwarenessContext позволяет использовать различные режимы масштабирования DPI в рамках одного процесса. До Windows 10 юбилейного обновления осведомленность о DPI в окне была привязана к режиму осведомленности о DPI на уровне процесса (без поддержки DPI, с учетом системного DPI или с учетом Per-Monitor DPI). Но теперь при использовании SetThreadDpiAwarenessContext окна верхнего уровня могут иметь режим осведомленности о DPI, отличный от режима информирования о DPI на уровне процесса. Это также влияет на дочерние окна, так как они всегда будут иметь тот же режим осведомленности о DPI, что и их родительское окно.

Использование SetThreadDpiAwarenessContext позволяет разработчикам решить, на чем они хотят сосредоточиться при разработке при определении поведения, зависяющего от DPI, для классических приложений. Например, основное окно верхнего уровня приложения можно масштабировать для каждого монитора, а дополнительные окна верхнего уровня можно масштабировать с помощью точечных изображений операционной системой.

Контекст осведомленности о DPI

До выхода SetThreadDpiAwarenessContext осведомленность о DPI для процесса определялась либо в манифесте двоичного файла приложения, либо через вызов SetProcessDpiAwareness во время инициализации процесса. С помощью SetThreadDpiAwarenessContext каждый поток может иметь отдельный контекст осведомленности о DPI, который может отличаться от контекста режима отслеживания DPI на уровне процесса. Контекст определения DPI потока представлен типом DPI_AWARENESS_CONTEXT и ведет себя следующим образом:

  • В потоке может быть изменен контекст осведомленности о DPI в любое время.
  • Все вызовы API, выполняемые после изменения контекста, будут выполняться в соответствующем контексте DPI (и могут быть виртуализированы).
  • При создании окна его осведомленность о DPI определяется как осведомленность о DPI вызывающего потока в это время.
  • При вызове процедуры окна поток автоматически переключается в контекст осведомленности о DPI, который использовался при создании окна.

Распространенный сценарий использования SetThreadDpiAwarenessContext выглядит следующим образом: начните с потока, выполняющегося с одним контекстом (например , DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE), временно переключитесь на другой контекст (DPI_AWARENESS_CONTEXT_UNAWARE), создайте окно и сразу же переключите контекст потока обратно в предыдущее состояние. Созданное окно будет иметь контекст DPI DPI_AWARENESS_CONTEXT_UNAWARE, а контекст вызывающего потока будет восстановлен в DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE с последующим вызовом Метода SetThreadDpiAwarenessContext. В этом сценарии окно, связанное с вызывающим потоком, будет выполняться с контекстом для каждого монитора (и, следовательно, не будет растягиваться операционной системой), в то время как только что созданное окно не будет учитывать DPI (и, следовательно, будет автоматически растянуто на дисплее >с 100 %-ным масштабированием).

На рисунке 1 показано, как поток процесса main выполняется с DPI_AWARENESS_CONTEXT_PER_MONITOR, переключает контекст на DPI_AWARENESS_CONTEXT_UNAWARE и создает новое окно. После этого созданное окно выполняется с контекстом осведомленности о DPI DPI_AWARENESS_CONTEXT_UNAWARE , когда ему отправляется сообщение или из него выполняются вызовы API. Сразу после создания нового окна поток main восстанавливается до предыдущего контекста DPI_AWARENESS_CONTEXT_PER_MONITOR.

схема, показывающая осведомленность о dpi для каждого монитора в действии

В дополнение к поддержке различных режимов информирования о DPI в рамках одного процесса, который предлагает SetThreadDpiAwarenessContext , для классических приложений были добавлены следующие функции DPI:

EnableNonClientDpiScaling

Примечание

Режим отслеживания DPI версии 2 автоматически включает эту функцию, поэтому вызов EnableNonClientDpiScaling не требуется в приложениях, использующих его.

Вызов EnableNonClientDpiScaling из обработчика WM_NCCREATE окна приведет к автоматическому масштабированию неклиентской области окна верхнего уровня для DPI. Если в окне верхнего уровня учитывается DPI для каждого монитора (так как сам процесс учитывает DPI для каждого монитора или потому, что окно было создано в потоке с поддержкой DPI для каждого монитора), подпись, полосы прокрутки, меню и строки меню этих окон будут масштабироваться при каждом изменении DPI окна.

Обратите внимание, что области дочернего окна, не являющиеся клиентскими, например не клиентские полосы прокрутки дочернего элемента управления редактированием, не будут автоматически масштабироваться при использовании этого API.

Примечание

EnableNonClientDpiScaling должен вызываться из обработчика WM_NCCREATE .

*API ForDpi
  • Некоторые часто используемые API, такие как GetSystemMetrics , не имеют контекста HWND и, следовательно, не имеют способа вывести правильное представление о DPI для возвращаемых значений. Вызов этих API из потока, работающего в другом режиме или контексте распознавания DPI, может возвращать значения, которые не масштабируются для контекста вызывающего потока. GetSystemMetricForDpi, SystemParametersInfoForDpi и AdjustWindowRectExForDpi будут выполнять те же функции, что и их коллеги, не знаюющие DPI, но принимают DPI в качестве аргумента и выводят сведения о dpi из контекста текущего потока.

  • GetSystemMetricForDpi и SystemParametersInfoForDpi возвращают значения системных метрик и системных параметров с масштабированием DPI в соответствии с этим уравнением:

    GetSystemMetrics(...) @ dpi == GetSystemMetricsForDpi(..., dpi)

    Таким образом, вызов GetSystemMetrics (или SystemParametersInfoForDpi) при выполнении на устройстве с определенным значением DPI будет возвращать то же значение, что и их варианты с поддержкой DPI (GetSystemMetricsForDpi и SystemParametersInfoForDpi) при том же значении DPI, что и входные данные.

  • AdjustWindowRectExForDpi принимает HWND и вычисляет требуемый размер прямоугольника окна с учетом DPI.

GetDpiForWindow
GetDpiForWindow вернет DPI, связанный с предоставленным HWND. Ответ будет зависеть от режима осведомленности о DPI для HWND:
Режим осведомленности О DPI для HWND Возвращаемое значение
Знают 96
Система Системный DPI
Per-Monitor DPI дисплея, на котором в основном находится связанное окно верхнего уровня.
(Если указано дочернее окно, возвращается значение DPI соответствующего родительского окна верхнего уровня.
GetDpiForSystem

Вызов GetDpiForSystem более эффективен, чем вызов GetDC и GetDeviceCaps для получения системного DPI.

Любой компонент, который может выполняться в приложении, использующего сведения о DPI подпроцесса, не должен предполагать, что системный DPI является статическим в течение жизненного цикла процесса. Например, если поток, выполняющийся в контексте DPI_AWARENESS_CONTEXT_UNAWARE осведомленности, запрашивает системный DPI, ответ будет равен 96. Однако если тот же поток переключится на DPI_AWARENESS_CONTEXT_SYSTEM контекст осведомленности и снова запросит системный DPI, ответ может быть другим. Чтобы избежать использования кэшированного (и, возможно, устаревшего) значения системного DPI, используйте GetDpiForSystem для получения системного DPI относительно режима осведомленности о DPI вызывающего потока.