Entwicklung von Desktopanwendungen mit hoher DPI-Auflösung unter Windows

Dieser Inhalt richtet sich an Entwickler, die Desktopanwendungen aktualisieren möchten, um Skalierungsfaktoränderungen (Punkte pro Zoll oder DPI) dynamisch zu verarbeiten, sodass ihre Anwendungen auf jeder Anzeige, auf der sie gerendert werden, scharf sein können.

Wenn Sie eine neue Windows-App von Grund auf neu erstellen möchten, wird dringend empfohlen, eine Universelle Windows-Plattform (UWP) Anwendung zu erstellen. UWP-Anwendungen werden automatisch – und dynamisch – für jede Anzeige skaliert, auf der sie ausgeführt werden.

Desktopanwendungen mit älteren Windows-Programmiertechnologien (unformatierte Win32-Programmierung, Windows Forms, Windows Presentation Framework (WPF) usw.) Die DPI-Skalierung kann ohne zusätzliche Entwicklerarbeit nicht automatisch verarbeitet werden. Ohne solche Maßnahmen werden Anwendungen in vielen gängigen Einsatzszenarien verschwommen oder in falscher Größe angezeigt. Dieses Dokument liefert Hintergrundinformationen darüber, was bei der Aktualisierung einer Desktopanwendung erforderlich ist, damit sie korrekt dargestellt wird.

Anzeigeskalierungsfaktor & DPI

Da die Displaytechnologie vorangekommen ist, haben Die Hersteller von Displaypanels eine zunehmende Anzahl von Pixeln in jede Einheit des physischen Raums auf ihren Panels gepackt. Dies hat dazu geführt, dass die Punkte pro Zoll (DPI) moderner Anzeigebereiche viel höher sind als bisher. In der Vergangenheit hatten die meisten Displays 96 Pixel pro linearem Zoll physischer Bildschirmfläche (96 DPI); 2017 sind Displays mit nahezu 300 DPI oder mehr problemlos erhältlich.

Die meisten Legacy-Desktop-UI-Frameworks verfügen über integrierte Annahmen, dass sich der DPI-Wert der Anzeige während der Lebensdauer des Prozesses nicht ändert. 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:

  • Setups mit mehreren Monitoren, bei denen jede Anzeige einen anderen Skalierungsfaktor aufweist und die Anwendung von einer Anzeige in eine andere verschoben wird (z. B. eine 4K- und eine 1080p-Anzeige)
  • Andocken und Abdocken eines Laptops mit hohem DPI-Wert mit einem externen Display 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 Anzeigeskalierungsfaktor, während Anwendungen ausgeführt werden

In diesen Szenarien zeichnen sich UWP-Anwendungen automatisch für den neuen DPI-Wert neu aus. Standardmäßig und ohne zusätzlichen Entwicklungsaufwand tun Desktopanwendungen dies nicht. Desktopanwendungen, die diesen zusätzlichen Aufwand nicht betreiben, um auf Änderungen der DPI zu reagieren, können dem Benutzer verschwommen oder in falscher Größe angezeigt werden.

DPI-Sensibilisierungsmodus

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:

Nicht DPI-fähig

Anwendungen, die DPI nicht unterstützen, werden mit einem festen DPI-Wert von 96 (100 %) gerendert. Wenn diese Anwendungen auf einem Bildschirm mit einer Anzeigeskala ausgeführt werden, die größer als 96 DPI ist, wird die Anwendungsbitmap von Windows auf die erwartete physische Größe gestreckt. Dies führt dazu, dass die Anwendung verschwommen angezeigt wird.

System-DPI-Bewusstsein

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ößenanpassungssteuerelemente, Auswählen von Schriftgraden, Laden von Ressourcen usw.) mithilfe dieses System-DPI-Werts. Daher werden Anwendungen mit System-DPI-Unterstützung von Windows auf Anzeigen, die mit genau diesem einen DPI-Wert rendern, nicht DPI-skaliert (d. h. per Bitmap-Streckung). Wenn die Anwendung auf eine Anzeige mit einem anderen Skalierungsfaktor verschoben wird oder sich der Skalierungsfaktor der Anzeige anderweitig ändert, skaliert Windows die Fenster der Anwendung per Bitmap-Skalierung, sodass sie verschwommen erscheinen. Effektiv rendern System-DPI-fähige Desktopanwendungen nur bei einem einzigen Skalierungsfaktor der Anzeige scharf und werden immer dann verschwommen, wenn sich der DPI-Wert ändert.

Per-Monitor und Per-Monitor (V2) DPI-Sensibilisierung

Es wird empfohlen, Dass Desktopanwendungen aktualisiert werden, um den Dpi-Sensibilisierungsmodus pro Monitor zu verwenden, sodass sie sofort korrekt gerendert werden können, wenn sich der DPI-Wert ändert. Wenn eine Anwendung Windows mitteilt, dass sie in diesem Modus ausgeführt werden soll, skaliert Windows die Anwendung bei einer Änderung des DPI-Werts nicht als Bitmap, sondern sendet stattdessen WM_DPICHANGED an das Anwendungsfenster. Die Anwendung ist dann vollständig selbst dafür verantwortlich, ihre Größe an den neuen DPI-Wert anzupassen. Die meisten Benutzeroberflächenframeworks, die von Desktopanwendungen verwendet werden (allgemeine Windows-Steuerelemente (comctl32), Windows Forms, Windows Presentation Framework usw.) Die automatische DPI-Skalierung wird nicht unterstützt, sodass Entwickler die Größe ändern und den Inhalt ihrer Fenster selbst neu positionieren müssen.

Es gibt zwei Versionen der Per-Monitor-Awareness, als die sich eine Anwendung registrieren kann: Version 1 und Version 2 (PMv2). Die Registrierung eines Prozesses als ausgeführt im PMv2-Sensibilisierungsmodus führt zu folgenden Ergebnissen:

  1. Die Anwendung, die benachrichtigt wird, wenn sich der DPI-Wert ändert (sowohl die HWNDs der obersten Ebene als auch untergeordnete HWNDs)
  2. Die Anwendung mit Zugriff auf die Rohpixel jeder Anzeige
  3. Die Anwendung, die von Windows niemals per Bitmap-Skalierung skaliert wird
  4. Automatische DPI-Skalierung des Nichtclientbereichs (Titelleiste, Bildlaufleisten usw.) durch Windows
  5. Win32-Dialoge (von CreateDialog) werden von Windows automatisch DPI-skaliert
  6. Designbasierte Bitmap-Ressourcen in Standardsteuerelementen (Kontrollkästchen, Schaltflächenhintergründe usw.), die automatisch mit dem entsprechenden DPI-Skalierungsfaktor gerendert werden

Wenn Anwendungen im Per-Monitor-v2-Awareness-Modus ausgeführt werden, werden sie benachrichtigt, wenn sich ihr DPI-Wert ändert. 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).

Anmerkung

Per-Monitor V1 (PMv1)-Bewusstsein ist sehr begrenzt. Es wird empfohlen, dass Anwendungen PMv2 verwenden.

Die folgende Tabelle zeigt, wie Anwendungen unter verschiedenen Szenarien gerendert werden:

DPI-Sensibilisierungsmodus Windows-Version eingeführt DPI-Ansicht der Anwendung Verhalten bei DPI-Änderung
Unwissend N/A Alle Displays sind 96 DPI Bitmap-Skalierung (verschwommen)
System Vista Alle Anzeigen weisen denselben DPI-Wert auf (der DPI-Wert der primären Anzeige zum Zeitpunkt des Starts der aktuellen Benutzersitzung) Bitmapdehnung (verschwommen)
Per-Monitor 8.1 Der DPI-Wert der Anzeige, auf der sich das Anwendungsfenster in erster Linie befindet
  • HWND der obersten Ebene wird über eine DPI-Änderung benachrichtigt.
  • Keine DPI-Skalierung von UI-Elementen.

Per-Monitor V2 Windows 10 Creators Update (1703) Der DPI-Wert der Anzeige, auf der sich das Anwendungsfenster in erster Linie befindet
  • der obersten Ebene und untergeordneten HWNDs werden über DPI-Änderungen benachrichtigt.

Automatische DPI-Skalierung von:
  • Nicht-Clientbereich
  • Vom Design gezeichnete Bitmaps in Standardsteuerelementen (comctl32 V6)
  • Dialogfelder (CreateDialog)

Dpi-Sensibilisierung pro Monitor (V1)

Per-Monitor V1 DPI-Sensibilisierungsmodus (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 Per-Monitor v2-Sensibilisierungsmodus 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:

  1. HWNDs der obersten Ebene werden über eine DPI-Änderung benachrichtigt und eine neue vorgeschlagene Größe bereitgestellt.
  2. Windows wird die Anwendungsbenutzeroberfläche nicht als Bitmap skalieren
  3. 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.

DPI-Skalierungsunterstützung pro Monitor nach UI-Framework/Technologie

Die folgende Tabelle zeigt die Unterstützung für die dpi-Sensibilisierung pro Monitor, die von verschiedenen Windows UI-Frameworks ab Windows 10 1703 angeboten wird:

Framework / Technologie Unterstützen Betriebssystemversion Dpi-Skalierung verarbeitet von Weitere Lektüre
Universelle Windows-Plattform (UWP) Voll 1607 Benutzeroberflächenframework universelle Windows-Plattform (UWP)
Raw Win32/Common Controls V6 (comctl32.dll)
  • Benachrichtigungen zu DPI-Änderungen, die an alle HWNDs gesendet werden
  • Vom Design gezeichnete Elemente werden in Standardsteuerelementen korrekt dargestellt
  • Automatische DPI-Skalierung für Dialogfelder
1703 Anwendung GitHub-Beispiel-
Windows Forms Eingeschränkte automatische DPI-Skalierung pro Monitor für einige Steuerelemente 1703 Benutzeroberflächenframework Unterstützung für hohen DPI-Wert in Windows Forms
Windows Presentation Framework (WPF) Native WPF-Anwendungen führen eine DPI-Skalierung für WPF durch, das in anderen Frameworks gehostet wird, und andere Frameworks, die in WPF gehostet werden, werden nicht automatisch skaliert 1607 Benutzeroberflächenframework GitHub-Beispiel-
GDI Nichts N/A Anwendung Siehe GDI-High-DPI-Skalierung
GDI+ Nichts N/A Anwendung Siehe GDI-High-DPI-Skalierung
MFC Nichts N/A Anwendung N/A

Aktualisieren vorhandener Anwendungen

Um eine vorhandene Desktopanwendung so zu aktualisieren, dass die DPI-Skalierung ordnungsgemäß verarbeitet werden kann, muss sie so aktualisiert werden, dass die wichtigen Teile der Benutzeroberfläche mindestens 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, vergrößert Windows die Benutzeroberfläche dieser Anwendungen per Bitmap-Skalierung, was häufig dazu führt, dass sie unscharf erscheint. Wenn eine system-DPI-fähige Anwendung zu einer pro-Monitor-DPI-fähigen Anwendung aktualisiert wird, muss der Code, der für das Layout der Benutzeroberfläche zuständig ist, so aktualisiert werden, dass er nicht nur während der Anwendungsinitialisierung ausgeführt wird, sondern auch immer dann, wenn eine DPI-Änderungsbenachrichtigung empfangen wird (WM_DPICHANGED bei Win32). 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 zurückgegeben 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:

Einheitliche DPI-Version Version pro Monitor
GetSystemMetrics GetSystemMetricsForDpi
AdjustWindowRectEx AdjustWindowRectExForDpi
SystemParametersInfo- SystemParametersInfoForDpi
GetDpiForMonitor GetDpiForWindow

Außerdem empfiehlt es sich, in Ihrer Codebasis nach hartcodierten Größen zu suchen, die von einem konstanten DPI-Wert ausgehen und durch Code ersetzt werden, der die DPI-Skalierung ordnungsgemäß übernimmt. Nachfolgend finden Sie ein Beispiel, das alle diese Vorschläge enthält:

Beispiel:

Das folgende Beispiel zeigt ein vereinfachtes Win32-Beispiel für die Erstellung 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:

  1. Der Code zur Fenstererstellung, der Position und Größe des untergeordneten HWND an die DPI des übergeordneten Fensters anpasst
  2. Auf DPI-Änderungen reagieren, indem das untergeordnete HWND neu positioniert und in der Größe angepasst wird
  3. 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; 

Beim Aktualisieren einer System-DPI-fähigen Anwendung sind einige häufige Schritte zu befolgen:

  1. Kennzeichnen Sie den Prozess mithilfe eines Anwendungsmanifests (oder mithilfe einer anderen Methode, abhängig von den verwendeten UI-Frameworks) als per-Monitor-DPI-fähig (V2).
  2. Machen Sie die Ui-Layoutlogik wiederverwendbar, und verschieben Sie sie aus dem Anwendungsinitialisierungscode, sodass sie beim Auftreten einer DPI-Änderung wiederverwendet werden kann (WM_DPICHANGED im Falle der Windows-Programmierung (Win32).
  3. Machen Sie allen Code ungültig, der davon ausgeht, dass DPI-abhängige Daten (DPI/Schriftarten/Größen/usw.) nie aktualisiert werden müssen. Es ist sehr üblich, Schriftgrade und DPI-Werte bei der Prozessinitialisierung zwischenzuspeichern. Wenn eine Anwendung so aktualisiert wird, dass sie DPI-bewusst pro Monitor ist, müssen DPI-sensitive Daten jedes Mal neu ausgewertet werden, wenn ein neuer DPI-Wert erkannt wird.
  4. Wenn eine DPI-Änderung auftritt, laden Sie alle Bitmap-Ressourcen für den neuen DPI-Wert neu (oder rasterisieren Sie sie neu) oder strecken Sie optional die aktuell geladenen Bitmap-Ressourcen auf die richtige Größe.
  5. Suchen Sie nach APIs, die nicht Per-Monitor-DPI-bewusst sind, und ersetzen Sie sie durch Per-Monitor-DPI-bewusste APIs (sofern zutreffend). Beispiel: Ersetzen Sie "GetSystemMetrics" durch "GetSystemMetricsForDpi".
  6. Testen Sie Ihre Anwendung auf einem Mehrfachanzeige-/Multi-DPI-System.
  7. Verwenden Sie für alle Fenster auf oberster Ebene in Ihrer Anwendung, die Sie nicht auf eine ordnungsgemäße DPI-Skalierung aktualisieren können, die DPI-Skalierung im gemischten Modus (siehe unten), um das Dehnung von Bitmaps dieser Fenster auf oberster Ebene durch das System zu ermöglichen.

Mischmodus-DPI-Skalierung (Unterprozess-DPI-Skalierung)

Beim Aktualisieren einer Anwendung zur Unterstützung des dpi-Bewusstseins 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 Ui-Code 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, den Einstieg in die Per-Monitor-DPI-Unterstützung zu erleichtern, indem es Ihnen ermöglicht, einige Ihrer Anwendungsfenster (nur Fenster der obersten Ebene) in ihrem ursprünglichen DPI-Bewusstseinsmodus auszuführen, während Sie Ihre Zeit und Energie darauf verwenden, die wichtigeren Teile Ihrer Benutzeroberfläche zu aktualisieren.

Nachfolgend sehen Sie eine Illustration, wie dies aussehen könnte: Sie aktualisieren die Benutzeroberfläche Ihrer Hauptanwendung („Hauptfenster“ in der Illustration), sodass sie mit Per-Monitor-DPI-Bewusstsein ausgeführt wird, während andere Fenster weiterhin in ihrem bisherigen Modus ausgeführt werden („Sekundärfenster“).

Unterschiede bei der DPI-Skalierung zwischen den Sensibilisierungsmodi

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 Fenster der obersten Ebene festgelegt werden. (untergeordneten Fenster müssen weiterhin mit der Skalierungsgröße ihres übergeordneten Elements übereinstimmen.) Ein Fenster auf oberster Ebene wird als Fenster ohne übergeordnetes Element definiert. Dies ist in der Regel ein "normales" Fenster mit Minimiert-, Maximierungs- und Schließen-Schaltflächen. Das Szenario, für das das DPI-Bewusstsein von Unterprozessen gedacht ist, besteht darin, dass die sekundäre Benutzeroberfläche von Windows skaliert wird (als Bitmap gestreckt), während Sie Ihre Zeit und Ressourcen darauf verwenden, Ihre Hauptbenutzeroberfläche zu aktualisieren.

Rufen Sie SetThreadDpiAwarenessContext- vor und nach allen Fenstererstellungsaufrufen auf, um die DPI-Sensibilisierung von Unterprozessen zu aktivieren. Das erstellte Fenster wird dem dpi-Bewusstsein zugeordnet, das Sie über SetThreadDpiAwarenessContext festgelegt haben. Verwenden Sie den zweiten Aufruf, um die DPI-Skalierungsbewusstseinseinstellung 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 der Natur der Komplexitäten verstehen, die sie einführen. Weitere Informationen zum DPI-Bewusstsein von Teilprozessen finden Sie unter Mixed-Mode-DPI-Skalierung und DPI-bewusste 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 der zu testenden Punkte sind:

  1. Verschieben von Anwendungsfenstern zwischen Anzeigen mit unterschiedlichen DPI-Werten hin und her
  2. Starten Ihrer Anwendung auf Anzeigen mit unterschiedlichen DPI-Werten
  3. Ändern des Skalierungsfaktors für Ihren Monitor während der Ausführung der Anwendung
  4. Ändern der Anzeige, die Sie als Hauptanzeige verwenden, Abmelden von Windows und anschließendes erneutes Testen Ihrer Anwendung nach der erneuten Anmeldung. Dies ist besonders hilfreich beim Suchen von Code, der hartcodierte Größen/Dimensionen verwendet.

Häufige Fallstricke (Win32)

Das in WM_DPICHANGED bereitgestellte vorgeschlagene Rechteck nicht verwenden

Wenn Windows an Ihr Anwendungsfenster eine WM_DPICHANGED-Meldung sendet, enthält diese Meldung ein empfohlenes Rechteck, das Sie verwenden sollten, um die Größe Ihres Fensters zu ändern. Es ist entscheidend, dass Ihre Anwendung dieses Rechteck verwendet, um ihre Größe daran anzupassen, da dies Folgendes bewirkt:

  1. Stellen Sie sicher, dass sich der Mauscursor beim Ziehen zwischen den Anzeigen in derselben relativen Position im Fenster befindet.
  2. 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 verhindern, dass Sie das vorgeschlagene Rechteck verwenden, das Windows in der WM_DPICHANGED Meldung 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 zu Virtualisierungs-

Wenn ein HWND oder ein Prozess entweder als DPI-unbewusst oder system-DPI-bewusst ausgeführt wird, kann es bzw. er von Windows per Bitmapskalierung skaliert werden. Wenn dies geschieht, skaliert und konvertiert Windows DPI-abhängige Informationen aus bestimmten APIs in das Koordinatensystem des aufrufenden Threads. Wenn beispielsweise ein Thread ohne DPI-Erkennung die Bildschirmgröße abfragt, während er auf einem Bildschirm mit hoher DPI ausgeführt wird, virtualisiert Windows die der Anwendung gegebene Antwort so, als ob der Bildschirm in 96-DPI-Einheiten angegeben wäre. Alternativ skaliert Windows, wenn ein System-DPI-bewusster Thread mit einer Anzeige mit einem anderen DPI-Wert interagiert als zu Beginn der aktuellen Benutzersitzung verwendet wurde, einige API-Aufrufe per DPI-Skalierung in den Koordinatenraum um, den das HWND verwenden würde, wenn es mit seinem ursprünglichen DPI-Skalierungsfaktor ausgeführt würde.

Wenn Sie Ihre Desktopanwendung auf eine ordnungsgemäße DPI-Skalierung aktualisieren, kann es schwierig sein zu wissen, welche API-Aufrufe virtualisierte Werte basierend auf dem Threadkontext zurückgeben können. diese Informationen sind derzeit nicht ausreichend von Microsoft dokumentiert. Beachten Sie, dass der Rückgabewert möglicherweise virtualisiert sein kann, wenn Sie eine System-API aus einem DPI-unbewussten oder system-DPI-bewussten Threadkontext 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 mit SetThreadDpiAwarenessContextdarauf, den alten Kontext wiederherzustellen, wenn Sie fertig sind, um ein falsches 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. Daher müssen Entwickler häufig zusätzlichen Aufwand betreiben, um die Skalierung DPI-abhängiger Informationen wie Größen, Punkten oder Symbolen zu handhaben. Beispielsweise müssen Entwickler, die LoadIcon verwenden, geladene Symbole entweder als Bitmaps strecken oder alternative APIs wie LoadImage verwenden, um Symbole in der für die jeweilige DPI korrekten Größe zu laden.

Ab Windows 11 (Build 22000) können Anwendungen, die Cursor aus Speicherdaten erstellen, setThreadCursorCreationScaling verwenden, um die automatische DPI-Skalierung pro Monitor zu ermöglichen, ähnlich wie Cursor aus Modulressourcen.

erzwungene Zurücksetzung des prozessweiten DPI-Bewusstseins

Im Allgemeinen kann der DPI-Sensibilisierungsmodus des Prozesses nach der Prozessinitialisierung nicht geändert werden. Windows kann jedoch den DPI-Aware-Modus Ihres Prozesses zwangsweise ändern, wenn Sie versuchen, gegen die Anforderung zu verstoßen, dass alle HWNDs in einer Fensterhierarchie denselben DPI-Aware-Modus haben. 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 Beziehung zwischen untergeordnetem und übergeordnetem Element zu erstellen, die gegen diese Regel verstößt, kann das DPI-Bewusstsein des gesamten Prozesses zurückgesetzt werden. Dies kann ausgelöst werden durch:

  1. Ein CreateWindow-Aufruf, bei dem das übergebene übergeordnete Fenster einen anderen DPI-Sensibilisierungsmodus aufweist als der aufrufende Thread.
  2. Ein 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:

Betrieb Windows 8.1 Windows 10 (1607 und früher) Windows 10 (1703 und höher)
CreateWindow (In-Proc) N/A Untergeordnetes Element erbt (gemischter Modus) Untergeordnetes Objekt erbt (gemischter Modus)
CreateWindow (Cross-Proc) Erzwungenes Zurücksetzen (des Prozesses des Aufrufers) Untergeordnetes Element erbt (gemischter Modus) Erzwungenes Zurücksetzen (des Prozesses des Aufrufers)
SetParent (In-Proc) N/A Erzwungenes Zurücksetzen (des aktuellen Prozesses) Fehler (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)

API-Referenz für hohe DPI-Werte

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