Entwicklung von Desktopanwendungen mit hoher DPI-Auflösung unter Windows
Dieser Inhalt richtet sich an Entwickler, die Desktopanwendungen aktualisieren möchten, um Änderungen des Skalierungsfaktors (Punkte pro Zoll oder DPI) dynamisch zu verarbeiten, damit ihre Anwendungen auf jedem Display, auf dem sie gerendert werden, gestochen scharf dargestellt werden.
Wenn Sie eine neue Windows-Anwendung von Grund auf neu erstellen möchten, wird dringend empfohlen, eine UWP-Anwendung (Universelle Windows-Plattform) zu erstellen. UWP-Anwendungen skalieren automatisch und dynamisch für jedes Display, auf dem sie ausgeführt werden.
Desktopanwendungen mit älteren Windows-Programmiertechnologien (unformatierte Win32-Programmierung, Windows Forms, Windows Presentation Framework (WPF) usw.) sind nicht in der Lage, die DPI-Skalierung ohne zusätzliche Entwicklerarbeit automatisch zu verarbeiten. Ohne diese Arbeit erscheinen Anwendungen in vielen gängigen Anwendungsszenarien unscharf oder in falscher Größe. In diesem Dokument finden Sie Informationen darüber, wie Sie eine Desktopanwendung aktualisieren können, damit sie ordnungsgemäß gerendert wird.
Skalierungsfaktor & DPI anzeigen
Mit den Fortschritten in der Displaytechnologie haben die Hersteller von Bildschirmen immer mehr Pixel in jede Einheit des physischen Platzes auf ihren Bildschirmen gepackt. Dies hat dazu geführt, dass die Punkte pro Zoll (DPI) moderner Anzeigebereiche viel mehr sind als in der Vergangenheit. In der Vergangenheit hatten die meisten Bildschirme 96 Pixel pro linearem Zoll (96 DPI). Im Jahr 2017 sind Bildschirme mit fast 300 DPI oder mehr problemlos erhältlich.
Die meisten Legacy-Desktop-UI-Frameworks gehen davon aus, dass sich die DPI des Bildschirms während der Laufzeit des Prozesses nicht ändern wird. Diese Annahme gilt nicht mehr, da sich die Anzeige-DPIs während der gesamten Lebensdauer eines Anwendungsprozesses häufig ändern. Einige häufige Szenarien, in denen sich der Skalierungsfaktor/DPI-Wert der Anzeige ändert:
- Konfigurationen mit mehreren Monitoren, bei denen jeder Bildschirm einen anderen Skalierungsfaktor hat und die Anwendung von einem Bildschirm zum anderen verschoben wird (z. B. ein 4K- und ein 1080p-Bildschirm)
- Andocken und Abdocken eines Laptops mit hohem DPI-Wert mit einem externen Bildschirm mit geringem DPI-Wert (oder umgekehrt)
- Herstellen einer Verbindung über Remotedesktop von einem Laptop/Tablet mit hohem DPI-Wert mit einem Gerät mit geringem DPI-Wert (oder umgekehrt)
- Ändern der Einstellungen für den Skalierungsfaktor während der Ausführung von Anwendungen
In diesen Szenarien werden UWP-Anwendungen automatisch für die neue DPI neu gezeichnet. Standardmäßig und ohne zusätzliche Entwicklerarbeit tun dies Desktopanwendungen nicht. Desktopanwendungen, die nicht auf DPI-Änderungen reagieren, können dem Benutzer unscharf oder in der falschen Größe erscheinen.
DPI-Erkennungsmodus
Desktopanwendungen müssen Windows mitteilen, ob sie die DPI-Skalierung unterstützen. Standardmäßig berücksichtigt das System, dass Desktopanwendungen DPI-Werte nicht kennen und die Fenster durch Bitmaps gestreckt werden. Durch Festlegen eines der folgenden verfügbaren DPI-Sensibilisierungsmodi können Anwendungen Windows explizit mitteilen, wie sie die DPI-Skalierung behandeln möchten:
DPI-Unaware
Dpi-Anwendungen rendern nicht mit einem festen DPI-Wert von 96 (100 %). Wenn diese Anwendungen auf einem Bildschirm mit einem Maßstab von mehr als 96 DPI ausgeführt werden, streckt Windows die Bitmap der Anwendung auf die erwartete physische Größe. Dies führt dazu, dass die Anwendung verschwommen angezeigt wird.
System-DPI-Sensibilisierung
Desktopanwendungen, die systemspezifische DPI-Werte kennen, erhalten in der Regel den DPI-Wert des primären verbundenen Monitors ab dem Zeitpunkt der Benutzeranmeldung. Während der Initialisierung gestalten sie ihre Benutzeroberfläche entsprechend (Größenanpassung der Steuerelemente, Auswahl der Schriftgrößen, Laden von Anlagen usw.) unter Verwendung dieses System-DPI-Werts. Daher werden System-DPI-fähige Anwendungen nicht von Windows skaliert (Bitmap-gestreckt) und zeigen das Rendering bei diesem einzelnen DPI-Wert an. Wenn die Anwendung auf einen Bildschirm mit einem anderen Skalierungsfaktor verschoben wird oder wenn sich der Skalierungsfaktor des Bildschirms anderweitig ändert, skaliert Windows die Fenster der Anwendung per Bitmap, wodurch sie unscharf erscheinen. System-DPI-fähige Desktop-Anwendungen werden nur bei einem einzigen Skalierungsfaktor scharf dargestellt und verschwimmen, sobald sich der DPI-Wert ändert.
DPI-Sensibilisierung pro Monitor und pro Monitor (V2)
Es wird empfohlen, dass Desktopanwendungen aktualisiert werden, um den DPI-Sensibilisierungsmodus pro Monitor zu verwenden, sodass sie sofort ordnungsgemäß gerendert werden können, wenn sich der DPI-Wert ändert. Wenn eine Anwendung Windows meldet, dass sie in diesem Modus ausgeführt werden soll, wird Windows die Anwendung bei einer DPI-Änderung nicht in der Bitmap strecken, sondern WM_DPICHANGED an das Anwendungsfenster senden. Es liegt dann vollständig in der Verantwortung der Anwendung, die Größenanpassung an die neue DPI selbst vorzunehmen. Die meisten Benutzeroberflächenframeworks, die von Desktopanwendungen verwendet werden (allgemeine Windows-Steuerelemente (comctl32), Windows Forms, Windows Presentation Framework usw.) unterstützen keine automatische DPI-Skalierung, sodass die Entwickler die Größe und Positionierung des Fensterinhalts selbst vornehmen müssen.
Es gibt zwei Versionen der Pro-Monitor-Sensibilisierung, dass eine Anwendung sich selbst registrieren kann: Version 1 und Version 2 (PMv2). Die Registrierung eines Prozesses als im PMv2 Sensibilisierungsmodus laufend führt zu:
- Die Anwendung wird über Änderungen des DPI informiert (sowohl die Top-Level- als auch die untergeordneten HWNDs)
- Die Anwendung sieht die rohen Pixel jeder Anzeige
- Die Anwendung wird von Windows nie als Bitmap skaliert
- Automatischer Nicht-Client-Bereich (Fensterbeschriftung, Bildlaufleisten usw.) DPI-Skalierung durch Windows
- Win32-Dialogfelder (aus CreateDialog) automatisch von Windows skaliert DPI
- Thematisch gezeichnete Bitmap-Anlagen in allgemeinen Steuerelementen (Kontrollkästchen, Schaltflächenhintergründe usw.) werden automatisch mit dem entsprechenden DPI-Skalierungsfaktor gerendert
Wenn Sie im Modus Pro-Monitor v2-Sensibilisierung arbeiten, werden Anwendungen benachrichtigt, wenn sich ihr DPI-Wert geändert hat. Wenn sich die Größe einer Anwendung für den neuen DPI-Wert nicht ändert, wird die Benutzeroberfläche der Anwendung zu klein oder zu groß angezeigt (abhängig von der Differenz in den vorherigen und neuen DPI-Werten).
Hinweis
Die Sensibilisierung pro Monitor V1 (PMv1) ist sehr begrenzt. Es wird empfohlen, dass Anwendungen PMv2 verwenden.
Die folgende Tabelle zeigt, wie Anwendungen unter verschiedenen Szenarien gerendert werden:
DPI-Erkennungsmodus | Windows-Version eingeführt | Ansicht der DPI-Werte der Anwendung | Verhalten bei DPI-Änderung |
---|---|---|---|
Nicht bewusst | N/V | Alle Anzeigen sind 96 DPI | Bitmapdehnung (unscharf) |
System | Vista | Alle Anzeigen weisen denselben DPI-Wert auf (der DPI-Wert der primären Anzeige zum Zeitpunkt des Starts der aktuellen Benutzersitzung) | Bitmapdehnung (unscharf) |
Pro Monitor | 8.1 | Der DPI-Wert der Anzeige, auf der sich das Anwendungsfenster in erster Linie befindet |
|
Pro Monitor V2 | Windows 10 Creators Update (1703) | Der DPI-Wert der Anzeige, auf der sich das Anwendungsfenster in erster Linie befindet |
Automatische DPI-Skalierung von:
|
DPI-Sensibilisierung pro Monitor (V1)
Der DPI-Sensibilisierungsmodus pro Monitor V1 (PMv1) wurde mit Windows 8.1 eingeführt. Dieser DPI-Sensibilisierungsmodus ist sehr begrenzt und bietet nur die unten aufgeführten Funktionen. Es wird empfohlen, dass Desktopanwendungen den Sensibilisierungsmodus pro Monitor v2 verwenden, der unter Windows 10 1703 oder höher unterstützt wird.
Die anfängliche Unterstützung für die Sensibilisierung pro Monitor bietet nur folgende Anwendungen:
- HWNDs der obersten Ebene werden über eine DPI-Änderung informiert und erhalten eine neue vorgeschlagene Größe
- Windows streckt die Benutzeroberfläche der Anwendung nicht per Bitmap
- Die Anwendung sieht alle Anzeigen in physischen Pixeln (siehe Virtualisierung)
Unter Windows 10 1607 oder höher können PMv1-Anwendungen auch EnableNonClientDpiScaling während WM_NCCREATE aufrufen, um anzufordern, dass Windows den Nicht-Clientbereich des Fensters ordnungsgemäß skaliert.
Pro Monitor DPI-Skalierungsunterstützung durch UI-Framework/Technologie
Die folgende Tabelle zeigt die Unterstützung für die DPI-Sensibilisierung pro Monitor, die von verschiedenen Windows Benutzeroberflächenframeworks ab Windows 10 1703 angeboten wird:
Framework/Technologie | Unterstützung | Betriebssystemversion | DPI-Skalierung verarbeitet von | Weitere nützliche Informationen |
---|---|---|---|---|
Universelle Windows-Plattform (UWP) | Vollzugriff | 1607 | Benutzeroberflächenframework | Universelle Windows-Plattform (UWP) |
Raw Win32/Common Controls V6 (comctl32.dll) |
|
1703 | Application | GitHub-Beispiel |
Windows Forms | Eingeschränkte automatische DPI-Skalierung pro Monitor für einige Steuerelemente | 1703 | Benutzeroberflächenframework | High DPI Support in Windows Forms (Unterstützung für hohe DPI-Werte in Windows Forms) |
Windows Presentation Framework (WPF) | Systemeigene WPF-Anwendungen skalieren WPF, die in anderen Frameworks gehostet werden, und andere in WPF gehostete Frameworks werden nicht automatisch skaliert | 1607 | Benutzeroberflächenframework | GitHub-Beispiel |
GDI | None | N/V | Application | Siehe GDI High-DPI-Skalierung |
GDI+ | None | N/V | Application | Siehe GDI High-DPI-Skalierung |
MFC | None | N/V | Application | N/V |
Aktualisieren bestehender Anwendungen
Um eine vorhandene Desktopanwendung so zu aktualisieren, dass die DPI-Skalierung ordnungsgemäß verarbeitet werden kann, muss sie so aktualisiert werden, dass zumindest die wichtigen Teile der Benutzeroberfläche aktualisiert werden, um auf DPI-Änderungen zu reagieren.
Die meisten Desktopanwendungen werden im System-DPI-Sensibilisierungsmodus ausgeführt. System-DPI-fähige Anwendungen skalieren in der Regel auf den DPI-Wert der primären Anzeige (die Anzeige, auf der sich die Taskleiste zum Zeitpunkt des Starts der Windows-Sitzung befand). Wenn sich der DPI-Wert ändert, streckt Windows die Benutzeroberfläche dieser Anwendungen per Bitmap, was oft zu Unschärfen führt. Wenn Sie eine DPI-fähige Anwendung so aktualisieren, dass sie DPI-fähig wird, muss der Code, der das Benutzeroberflächen-Layout handhabt, so aktualisiert werden, dass er nicht nur während der Initialisierung der Anwendung ausgeführt wird, sondern auch immer dann, wenn eine DPI-Änderungsbenachrichtigung (WM_DPICHANGED im Fall von Win32) empfangen wird. Dies umfasst in der Regel das Überarbeiten aller Annahmen im Code, dass die Benutzeroberfläche nur einmal skaliert werden muss.
Im Falle der Win32-Programmierung verfügen viele Win32-APIs nicht über einen DPI- oder Anzeigekontext, sodass nur Werte relativ zum System-DPI wiedergegeben werden. Es kann hilfreich sein, den Code zu durchlaufen, um nach einigen dieser APIs zu suchen und sie durch DPI-fähige Varianten zu ersetzen. Einige der gängigen APIs mit DPI-fähigen Varianten sind:
Einzelne DPI-Version | Version pro Monitor |
---|---|
GetSystemMetrics | GetSystemMetricsForDpi |
AdjustWindowRectEx | AdjustWindowRectExForDpi |
SystemParametersInfo | SystemParametersInfoForDpi |
GetDpiForMonitor | GetDpiForWindow |
Es ist auch eine gute Idee, nach fest kodierten Größen in Ihrer Codebasis zu suchen, die von einer konstanten DPI ausgehen, und diese durch Code zu ersetzen, der die DPI-Skalierung ordnungsgemäß berücksichtigt. Nachfolgend finden Sie ein Beispiel, das alle diese Vorschläge enthält:
Beispiel:
Das folgende Beispiel zeigt einen vereinfachten Win32-Fall zum Erstellen eines untergeordneten HWND. Der Aufruf von CreateWindow geht davon aus, dass die Anwendung mit 96 DPI (USER_DEFAULT_SCREEN_DPI
Konstante) ausgeführt wird, und weder die Größe der Schaltfläche noch die Position der Schaltfläche werden bei höheren DPIs korrekt sein:
case WM_CREATE:
{
// Add a button
HWND hWndChild = CreateWindow(L"BUTTON", L"Click Me",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
50,
50,
100,
50,
hWnd, (HMENU)NULL, NULL, NULL);
}
Der folgende aktualisierte Code zeigt:
- Die DPI-Skalierung des Fenstererstellungscodes für die Position und Größe des untergeordneten HWND für den DPI-Wert des übergeordneten Fensters
- Reagieren auf DPI-Änderungen durch Ändern der Position und Größenänderung des untergeordneten HWND
- Hartcodierte Größen wurden entfernt und durch Code ersetzt, der auf DPI-Änderungen reagiert
#define INITIALX_96DPI 50
#define INITIALY_96DPI 50
#define INITIALWIDTH_96DPI 100
#define INITIALHEIGHT_96DPI 50
// DPI scale the position and size of the button control
void UpdateButtonLayoutForDpi(HWND hWnd)
{
int iDpi = GetDpiForWindow(hWnd);
int dpiScaledX = MulDiv(INITIALX_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI);
int dpiScaledY = MulDiv(INITIALY_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI);
int dpiScaledWidth = MulDiv(INITIALWIDTH_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI);
int dpiScaledHeight = MulDiv(INITIALHEIGHT_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI);
SetWindowPos(hWnd, hWnd, dpiScaledX, dpiScaledY, dpiScaledWidth, dpiScaledHeight, SWP_NOZORDER | SWP_NOACTIVATE);
}
...
case WM_CREATE:
{
// Add a button
HWND hWndChild = CreateWindow(L"BUTTON", L"Click Me",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
0,
0,
0,
0,
hWnd, (HMENU)NULL, NULL, NULL);
if (hWndChild != NULL)
{
UpdateButtonLayoutForDpi(hWndChild);
}
}
break;
case WM_DPICHANGED:
{
// Find the button and resize it
HWND hWndButton = FindWindowEx(hWnd, NULL, NULL, NULL);
if (hWndButton != NULL)
{
UpdateButtonLayoutForDpi(hWndButton);
}
}
break;
Wenn Sie eine DPI-fähige Anwendung aktualisieren, sollten Sie folgende gängigen Schritte befolgen:
- Markieren Sie den Prozess je nach Monitor-DPI-fähig (V2) mithilfe eines Anwendungsmanifests (oder einer anderen Methode, abhängig von den verwendeten Benutzeroberflächenframeworks).
- Machen Sie die Benutzeroberflächen-Layoutlogik wiederverwendbar und verschieben Sie diese aus dem Anwendungsinitialisierungscode, sodass sie beim Auftreten einer DPI-Änderung wiederverwendet werden kann (WM_DPICHANGED im Falle der Windows-Programmierung (Win32).
- Ungültiger Code, der davon ausgeht, dass DPI-sensitive Daten (DPI/Schriftarten/Größen/usw.) nie aktualisiert werden müssen. Es ist eine sehr gängige Praxis, Schriftgrößen und DPI-Werte bei der Prozessinitialisierung zwischenzuspeichern. Wenn Sie eine Anwendung so aktualisieren, dass sie DPI-kompatibel ist, müssen DPI-sensitive Daten jedes Mal neu ausgewertet werden, wenn ein neuer DPI auftritt.
- Wenn eine DPI-Änderung auftritt, laden Sie alle Bitmap-Anlagen für den neuen DPI-Wert neu (oder neu rastern) oder optional die aktuell geladenen Ressourcen auf die richtige Größe.
- Suchen Sie nach APIs, die nicht Pro-Monitor-DPI-fähig sind und ersetzen Sie diese durch Pro-Monitor-DPI-fähige APIs (sofern zutreffend). Beispiel: Ersetzen Sie „GetSystemMetrics“ durch „GetSystemMetricsForDpi“.
- Testen Sie Ihre Anwendung auf einem Mehrfachanzeige-/Multi-DPI-System.
- Für alle obersten Fenster in Ihrer Anwendung, die Sie nicht aktualisieren können, um eine korrekte DPI-Skalierung vorzunehmen, verwenden Sie die DPI-Skalierung im gemischten Modus (wie unten beschrieben), um die Erstreckung von Bitmaps dieser obersten Fenster durch das System zu ermöglichen.
DPI-Skalierung im gemischten Modus (DPI-Skalierung von Teilprozessen)
Beim Aktualisieren einer Anwendung zur Unterstützung der DPI-Sensibilisierung pro Monitor kann es manchmal unpraktisch oder unmöglich werden, jedes Fenster in der Anwendung einzeln zu aktualisieren. Dies kann einfach auf die Zeit und den Aufwand zurückzuführen sein, die zum Aktualisieren und Testen der gesamten Benutzeroberfläche erforderlich sind, oder weil Sie nicht über den gesamten Benutzeroberflächencode verfügen, den Sie ausführen müssen (wenn Ihre Anwendung möglicherweise die Benutzeroberfläche von Drittanbietern lädt). In diesen Situationen bietet Windows eine Möglichkeit, die Welt der Sensibilisierung pro Monitor zu vereinfachen, indem Sie einige Ihrer Anwendungsfenster (nur auf oberster Ebene) im ursprünglichen DPI-Sensibilisierungsmodus ausführen können, während Sie sich auf Ihre Zeit und Energie konzentrieren, um die wichtigeren Teile Ihrer Benutzeroberfläche zu aktualisieren.
Unten sehen Sie eine Illustration, wie dies aussehen könnte: Sie aktualisieren die Benutzeroberfläche Ihrer Hauptanwendung („Hauptfenster“ in der Illustration) so, dass sie mit einer DPI-Sensibilisierung für jeden Monitor läuft, während Sie andere Fenster in ihrem bestehenden Modus ausführen („Sekundärfenster“).
Vor dem Windows 10 Anniversary Update (1607) war der DPI-Sensibilisierungsmodus eines Prozesses eine prozessweite Eigenschaft. Ab dem Windows 10 Anniversary Update kann diese Eigenschaft jetzt pro oberstem Fenster festgelegt werden. (Untergeordnete Fenster müssen weiterhin mit der Skalierungsgröße des übergeordneten Fensters übereinstimmen.) Ein oberstes Fenster wird als Fenster ohne übergeordnetes Element definiert. Dies ist in der Regel ein „normales“ Fenster mit Schaltflächen zum Minimieren, Maximieren und Schließen. Das Szenario, für das die DPI-Sensibilisierung für untergeordnete Prozesse vorgesehen ist, besteht darin, dass die sekundäre Benutzeroberfläche von Windows skaliert (Bitmap gestreckt) werden soll, während Sie sich auf die Zeit und Ressourcen konzentrieren, um die primäre Benutzeroberfläche zu aktualisieren.
Rufen Sie SetThreadDpiAwarenessContext vor und nach allen Fenstererstellungsaufrufen auf, um die DPI-Sensibilisierung von Unterprozessen zu aktivieren. Das erstellte Fenster wird der DPI-Sensibilisierung zugeordnet, die Sie über SetThreadDpiAwarenessContext festgelegt haben. Verwenden Sie den zweiten Aufruf, um den DPI-Grad des aktuellen Threads wiederherzustellen.
Während Sie die DPI-Skalierung von Unterprozessen verwenden, können Sie sich auf Windows verlassen, um einige der DPI-Skalierungen für Ihre Anwendung durchzuführen, wodurch die Komplexität Ihrer Anwendung erhöht werden kann. Es ist wichtig, dass Sie die Nachteile dieses Ansatzes und die Komplexität, die er mit sich bringt, verstehen. Weitere Informationen zur DPI-Sensibilisierung für Teilprozesse finden Sie unter DPI-Skalierung im gemischten Modus und DPI-fähige APIs.
Testen Ihrer Änderungen
Nachdem Sie Ihre Anwendung aktualisiert haben, damit die DPI-Werte pro Monitor beachtet werden, ist es wichtig, dass Ihre Anwendung ordnungsgemäß auf DPI-Änderungen in einer Umgebung mit gemischten DPI-Werten reagiert. Einige Besonderheiten, die Sie testen sollten, sind:
- Hin- und Herbewegen von Anwendungsfenstern zwischen Anzeigen mit unterschiedlichen DPI-Werten
- Starten der Anwendung auf Anzeige unterschiedlicher DPI-Werte
- Ändern des Skalierungsfaktors für Ihren Monitor während der Ausführung der Anwendung
- Ändern Sie die Anzeige, die Sie als primären Bildschirm verwenden, melden Sie sich von Windows ab und testen Sie Ihre Anwendung erneut, nachdem Sie sich wieder angemeldet haben. Dies ist besonders hilfreich beim Suchen von Code, der hartcodierte Größen/Dimensionen verwendet.
Häufige Fallstricke (Win32)
Verwenden Sie nicht das vorgeschlagene Rechteck, das in WM_DPICHANGED angegeben ist
Wenn Windows Ihrem Anwendungsfenster eine WM_DPICHANGED-Nachricht sendet, enthält diese Nachricht ein vorgeschlagenes Rechteck, das Sie zur Größenänderung Ihres Fensters verwenden sollten. Es ist wichtig, dass Ihre Anwendung dieses Rechteck verwendet, um die Größe zu ändern, da dies zu einer Änderung führt:
- Stellen Sie sicher, dass der Mauszeiger in der gleichen relativen Position auf dem Fenster bleibt, wenn Sie zwischen den Anzeigen ziehen
- Verhindern Sie, dass das Anwendungsfenster in einen rekursiven DPI-Änderungszyklus wechselt, in dem eine DPI-Änderung eine nachfolgende DPI-Änderung auslöst, wodurch eine weitere DPI-Änderung ausgelöst wird.
Wenn Sie anwendungsspezifische Anforderungen haben, die Sie daran hindern, das vorgeschlagene Rechteck zu verwenden, das Windows in der Meldung WM_DPICHANGED bereitstellt, lesen Sie WM_GETDPISCALEDSIZE. Diese Meldung kann verwendet werden, um Windows eine gewünschte Größe zuzuweisen, die Sie verwenden möchten, nachdem die DPI-Änderung aufgetreten ist, während die oben beschriebenen Probleme weiterhin vermieden werden.
Fehlende Dokumentation zur Virtualisierung
Wenn eine HWND oder ein Prozess entweder als DPI-unbewusst oder als System-DPI-bewusst ausgeführt wird, kann Windows eine Bitmap-Dehnung vornehmen. In diesem Fall skaliert und konvertiert Windows DPI-sensitive Informationen aus einigen APIs in den Koordinatenbereich des aufrufenden Threads. Wenn z. B. ein nicht DPI-fähiger Thread die Bildschirmgröße abfragt, während er auf einem Bildschirm mit hohem DPI-Wert läuft, virtualisiert Windows die Antwort an die Anwendung so, als ob der Bildschirm 96 DPI-Einheiten hätte. Wenn ein System-DPI-fähiger Thread mit einem Bildschirm mit einem anderen DPI-Wert interagiert als dem, der beim Start der Sitzung des aktuellen Benutzers verwendet wurde, skaliert Windows einige API-Aufrufe in den Koordinatenbereich, den das HWND verwenden würde, wenn es mit seinem ursprünglichen DPI-Skalierungsfaktor laufen würde.
Wenn Sie Ihre Desktopanwendung aktualisieren, um die DPI-Skalierung ordnungsgemäß durchzuführen, kann es schwierig sein zu wissen, welche API-Aufrufe auf der Grundlage des Thread-Kontexts virtualisierte Werte wiedergeben können. Diese Informationen sind derzeit von Microsoft nicht ausreichend dokumentiert. Beachten Sie, dass der Rückgabewert virtualisiert sein kann, wenn Sie eine System-API aus einem DPI-unbewussten oder System-DPI-bewussten Thread-Kontext aufrufen. Stellen Sie daher sicher, dass ihr Thread im DPI-Kontext ausgeführt wird, den Sie bei der Interaktion mit dem Bildschirm oder einzelnen Fenstern erwarten. Achten Sie beim vorübergehenden Ändern des DPI-Kontexts eines Threads mithilfe von SetThreadDpiAwarenessContextdarauf, den alten Kontext wiederherzustellen, wenn Sie damit fertig sind, um ein fehlerhaftes Verhalten an anderer Stelle in Ihrer Anwendung zu vermeiden.
Viele Windows-APIs verfügen nicht über einen DPI-Kontext
Viele ältere Windows-APIs enthalten keinen DPI- oder HWND-Kontext als Teil ihrer Schnittstelle. Infolgedessen müssen Entwickler oft zusätzliche Arbeit leisten, um die Skalierung von DPI-sensitiven Informationen wie Größen, Punkten oder Symbolen zu handhaben. So müssen Entwickler, die LoadIcon verwenden, entweder geladene Symbole per Bitmap strecken oder alternative APIs verwenden, um Symbole in der richtigen Größe für die entsprechende DPI zu laden, wie z. B. LoadImage.
Erzwungenes Zurücksetzen der prozessweiten DPI-Sensibilisierung
Im Allgemeinen kann der DPI-Sensibilisierungsmodus des Prozesses nach der Prozessinitialisierung nicht geändert werden. Windows kann jedoch den DPI-Sensibilisierungsmodus Ihres Prozesses zwangsweise ändern, wenn Sie versuchen, die Anforderung zu unterbrechen, dass alle HWNDs in einer Fensterstruktur den gleichen DPI-Sensibilisierungsmodus aufweisen. In allen Versionen von Windows ab Windows 10 1703 ist es nicht möglich, unterschiedliche HWNDs in einer HWND-Struktur in verschiedenen DPI-Sensibilisierungsmodi auszuführen. Wenn Sie versuchen, eine untergeordnete Beziehung zu erstellen, die gegen diese Regel verstößt, kann der DPI-Grad des gesamten Prozesses zurückgesetzt werden. Dies kann ausgelöst werden durch:
- Einen CreateWindow-Aufruf, bei dem das übergebene übergeordnete Fenster einen anderen DPI-Sensibilisierungsmodus aufweist als der aufrufende Thread.
- Einen SetParent-Aufruf, bei dem die beiden Fenster unterschiedlichen DPI-Sensibilisierungsmodi zugeordnet sind.
Die folgende Tabelle zeigt, was passiert, wenn Sie versuchen, diese Regel zu verletzen:
Vorgang | Windows 8.1 | Windows 10 (1607 und älter) | Windows 10 (1703 und höher) |
---|---|---|---|
CreateWindow (In-Proc) | N/V | Untergeordnetes Erben (gemischter Modus) | Untergeordnetes Erben (gemischter Modus) |
CreateWindow (Cross-Proc) | Erzwungenes Zurücksetzen (des Prozesses des Aufrufers) | Untergeordnetes Erben (gemischter Modus) | Erzwungenes Zurücksetzen (des Prozesses des Aufrufers) |
SetParent (In-Proc) | N/V | Erzwungenes Zurücksetzen (des aktuellen Prozesses) | Fail (ERROR_INVALID_STATE) |
SetParent (Cross-Proc) | Erzwungenes Zurücksetzen (des Prozesses des untergeordneten Fensters) | Erzwungenes Zurücksetzen (des Prozesses des untergeordneten Fensters) | Erzwungenes Zurücksetzen (des Prozesses des untergeordneten Fensters) |