Mixed-Mode DPI-Skalierung und DPI-fähige APIs

Sub-Process DPI-Unterstützung

SetThreadDpiAwarenessContext ermöglicht die Verwendung verschiedener DPI-Skalierungsmodi innerhalb eines einzelnen Prozesses. Vor dem Windows 10 Anniversary Update wurde die DPI-Empfindlichkeit eines Fensters an den prozessweiten DPI-Awareness-Modus gebunden (DPI-nicht bekannt, System-DPI-fähigen oder Per-Monitor DPI-fähigen). Mit SetThreadDpiAwarenessContext können Fenster der obersten Ebene jedoch einen DPI-Awareness-Modus aufweisen, der sich von dem des prozessweiten DPI-Erkennungsmodus unterscheidet. Dies wirkt sich auch auf untergeordnete Fenster aus, da sie immer den gleichen DPI-Wahrnehmungsmodus wie ihr übergeordnetes Fenster aufweisen.

Die Verwendung von SetThreadDpiAwarenessContext ermöglicht Es Entwicklern, zu entscheiden, wo sie ihre Entwicklungsarbeit beim Definieren des DPI-spezifischen Verhaltens für Desktopanwendungen konzentrieren möchten. Beispielsweise kann das primäre Fenster der obersten Ebene einer Anwendung pro Monitor skaliert werden, während sekundäre Fenster der obersten Ebene durch Bitmapskalierung durch das Betriebssystem skaliert werden können.

Der DPI-Kontext für die Wahrnehmung

Vor der Verfügbarkeit von SetThreadDpiAwarenessContext wurde die DPI-Kenntnis eines Prozesses entweder im Manifest der Anwendungsbinärdatei oder über einen Aufruf von SetProcessDpiAwareness während der Prozessinitialisierung definiert. Mit SetThreadDpiAwarenessContext kann jeder Thread einen einzelnen DPI-Awareness-Kontext aufweisen, der sich von dem des prozessweiten DPI-Wahrnehmungsmodus unterscheiden kann. Der DPI-Wahrnehmungskontext eines Threads wird mit dem Typ "DPI _ AWARENESS _ CONTEXT"- dargestellt und verhält sich wie folgt:

  • Der DPI-Kontext eines Threads kann jederzeit geändert werden.
  • Alle API-Aufrufe, die ausgeführt werden, nachdem der Kontext geändert wurde, werden im entsprechenden DPI-Kontext ausgeführt (und können virtualisiert werden).
  • Wenn ein Fenster erstellt wird, wird seine DPI-Wahrnehmung als DPI-Kenntnis des aufrufenden Threads zu diesem Zeitpunkt definiert.
  • Wenn die Fensterprozedur für ein Fenster aufgerufen wird, wird der Thread automatisch in den DPI-Kontext gewechselt, der beim Erstellen des Fensters verwendet wurde.

Ein häufiges Szenario für die Verwendung von SetThreadDpiAwarenessContext ist: Beginnen Sie mit einem Thread, der mit einem Kontext ausgeführt wird (z. B. DPI _ AWARENESS CONTEXT PER MONITOR _ _ _ _ AWARE), wechseln Sie vorübergehend zu einem anderen Kontext (DPI AWARENESS CONTEXT _ _ _ UNAWARE), erstellen Sie ein Fenster, und wechseln Sie dann sofort wieder zum vorherigen Zustand des Threadkontexts. Das erstellte Fenster hat den DPI-Kontext DPI _ AWARENESS CONTEXT _ _ UNAWARE, während der Kontext des aufrufenden Threads mit einem nachfolgenden Aufruf von SetThreadDpiAwarenessContext in DPI AWARENESS CONTEXT PER _ _ MONITOR _ _ _ AWARE wiederhergestellt wird. In diesem Szenario würde das Fenster, das dem aufrufenden Thread zugeordnet ist, mit einem Pro-Monitor-Kontext ausgeführt (und daher nicht vom Betriebssystem per Bitmap gestreckt werden), während das neu erstellte Fenster nicht DPI-bewusst ist (und daher automatisch eine Bitmap gestreckt wird, die auf >100%ige Skalierung festgelegt ist).

Abbildung 1 zeigt, wie der Hauptprozessthread mit DPI _ AWARENESS CONTEXT _ PER _ _ MONITOR ausgeführt wird, seinen Kontext in DPI AWARENESS CONTEXT _ _ _ UNAWARE umschaltet und ein neues Fenster erstellt. Das neu erstellte Fenster wird dann mit einem DPI-Awareness-Kontext von DPI _ AWARENESS CONTEXT _ _ UNAWARE ausgeführt, wenn eine Nachricht an sie gesendet wird oder API-Aufrufe daraus erfolgen. Unmittelbar nach dem Erstellen des neuen Fensters wird der Hauptthread im vorherigen Kontext von DPI _ AWARENESS CONTEXT PER _ _ _ MONITOR wiederhergestellt.

Diagramm, das die DPI-Überwachung pro Monitor in Aktion zeigt

Zusätzlich zur Unterstützung verschiedener DPI-Awareness-Modi innerhalb eines einzelnen Prozesses, der von SetThreadDpiAwarenessContext angeboten wird, wurden die folgenden DPI-spezifischen Funktionen für Desktopanwendungen hinzugefügt:

EnableNonClientDpiScaling

Hinweis

Der DPI-Modus "Pro Monitor V2" aktiviert diese Funktion automatisch, und das Aufrufen von EnableNonClientDpiScaling ist daher in Anwendungen, die sie verwenden, nicht erforderlich.

Der Aufruf von EnableNonClientDpiScaling innerhalb des WM _ NCCREATE-Handlers eines Fensters führt dazu, dass der Nicht-Clientbereich eines Fensters der obersten Ebene automatisch für DPI skaliert wird. Wenn das Fenster der obersten Ebene monitorspezifische DPI-fähige Daten enthält (unabhängig davon, ob der Prozess selbst monitorspezifische DPI-fähige Vorgänge ist oder weil das Fenster in einem DPI-fähigen Thread pro Monitor erstellt wurde), werden die Beschriftungsleiste, Scrollleisten, Menüs und Menüleisten dieser Fenster bei jeder Änderung des Fenster-DPI-Werts DPI-skaliert.

Beachten Sie, dass Nicht-Clientbereiche eines untergeordneten Fensters, z. B. Nicht-Client-Scrollleisten eines untergeordneten Bearbeitungssteuerelements, nicht automatisch DPI skaliert werden, wenn diese API verwendet wird.

Hinweis

EnableNonClientDpiScaling muss vom WM _ NCCREATE-Handler aufgerufen werden.

Die *ForDpi-APIs
  • Mehrere häufig verwendete APIs wie GetSystemMetrics haben keinen Kontext eines HWND und können daher nicht das richtige DPI-Bewusstsein für ihre Rückgabewerte ableiten. Das Aufrufen dieser APIs über einen Thread, der in einem anderen DPI-Awareness-Modus oder -Kontext ausgeführt wird, gibt möglicherweise Werte zurück, die nicht für den Kontext des aufrufenden Threads skaliert werden. "GetSystemMetricForDpi", "SystemParametersInfoForDpi"und "AdjustWindowRectExForDpi" führen die gleiche Funktionalität wie ihre DPI-fremden Entsprechungen aus, verwenden aber einen DPI als Argument und ziehen die DPI-Wahrnehmung aus dem Kontext des aktuellen Threads her.

  • GetSystemMetricForDpi und SystemParametersInfoForDpi geben DPI-skalierte Systemmetrikwerte und Systemparameterwerte gemäß dieser Gleichung zurück:

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

    Daher gibt der Aufruf von GetSystemMetrics (oder SystemParametersInfoForDpi) während der Ausführung auf einem Gerät mit einem bestimmten System-DPI-Wert denselben Wert zurück, den die DPI-fähigen Varianten (GetSystemMetricsForDpi und SystemParametersInfoForDpi) erhalten, wenn derselbe DPI-Wert wie die Eingabe angegeben wird.

  • AdjustWindowRectExForDpi nimmt einen HWND an und berechnet die erforderliche Größe eines Fensterrechtecks auf DPI-bezogene Weise.

GetDpiForWindow
GetDpiForWindow gibt den DPI zurück, der dem bereitgestellten HWND zugeordnet ist. Die Antwort hängt vom DPI-Awareness-Modus des HWND ab:
DPI-Awareness-Modus von HWND Rückgabewert
Nicht bekannt 96
System Der System-DPI
Per-Monitor Der DPI der Anzeige, in der sich das zugeordnete Fenster der obersten Ebene in erster Linie befindet
(Wenn ein untergeordnetes Fenster bereitgestellt wird, wird der DPI des entsprechenden übergeordneten Fensters der obersten Ebene zurückgegeben.)
GetDpiForSystem

Das Aufrufen von GetDpiForSystem ist effizienter als das Aufrufen von GetDC und GetDeviceCaps, um den System-DPI abzurufen.

Jede Komponente, die in einer Anwendung ausgeführt werden könnte, die DPI-Informationen des Unterprozesses verwendet, sollte nicht davon ausgehen, dass der System-DPI während des Lebenszyklus des Prozesses statisch ist. Wenn z. B. ein Thread, der unter dem DPI _ AWARENESS CONTEXT _ UNAWARE _ Awareness-Kontext ausgeführt wird, den System-DPI abfragt, lautet die Antwort 96. Wenn jedoch derselbe Thread zum Kontext der DPI _ AWARENESS _ _ CONTEXT-SYSTEMerkennung gewechselt und den System-DPI erneut abgefragt hat, kann die Antwort anders lauten. Um die Verwendung eines zwischengespeicherten (und möglicherweise veralteten) System-DPI-Werts zu vermeiden, verwenden Sie GetDpiForSystem, um den System-DPI relativ zum DPI-Wahrnehmungsmodus des aufrufenden Threads abzurufen.