DPI Awareness – Unaware in one Release, System Aware in the Other [duplicate]

About the problem reported in this Question:
An application, which is DPI-unaware by design, relying on Windows virtualization to scale its UI content, suddenly (although after some modifications, leading to a minor release update) – and apparently without an observable reason – becomes DPI-Aware (System Aware).

  • The application also relies on an interpretation of the app.manifest <windowsSettings>, where the absence of a DPI-awareness definition, defaults (for backward compatibility) to DPI-Unaware.

  • There are no direct references to WPF assemblies and no DPI-related API calls.

  • The application includes third-party components (and, possibly, external dependencies).


Since DPI-Awareness has become a relevant aspect of UI presentation, given the diversity of screens resolutions available (and related DPI scaling settings), most component producers have adapted to High-DPI and their products are DPI-Aware (scale when a DPI change is detected) and make use of DPI-Aware assemblies (often referencing WPF assemblies, DPI-Aware by definition).

When one of these DPI-Aware components is referenced in a project (directly or indirectly), a DPI-Unaware application will become DPI-Aware, when DPI-Awareness has not been disabled explicitly.

The more direct (and recommended) method to declare an assembly DPI-Awareness, is to declare it explicitly in the application manifest.

Refer to Hans Passant’s answer for an application manifest setting prior to Visual Studio 2017:
How to configure an app to run on a machine with a high DPI setting

Since Visual Studio 2015-Upd.1, this setting is already present in app.manifest, it just needs to be uncommented. Set the section: <dpiAware>false</dpiAware>.

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>

  //(...)
   
  <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
       DPI. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need 
       to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should 
       also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
    </windowsSettings>
  </application>

//(...)

</assembly>

Refer to these MSDN articles for more information:
High DPI desktop application development on Windows
Setting the default DPI awareness for a process

Another method is to set the process context DPI-Awareness using these Windows API functions:

Windows 7
SetProcessDPIAware

[DllImport("user32.dll", SetLastError=true)]
static extern bool SetProcessDPIAware();

Windows 8.1
SetProcessDpiAwareness

[DllImport("shcore.dll")]
static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);

enum ProcessDPIAwareness
{
    DPI_Unaware = 0,
    System_DPI_Aware = 1,
    Per_Monitor_DPI_Aware = 2
}

Windows 10, version 1703
SetProcessDpiAwarenessContext()
(When opting for a Per-Monitor DPI-Awareness, use Context_PerMonitorAwareV2)

Also see: Mixed-Mode DPI Scaling and DPI-aware APIs – MSDN

Windows 10, version 1809 (October 2018)
A new DPI_AWARENESS_CONTEXT has been added: DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED

DPI unaware with improved quality of GDI-based content. This mode
behaves similarly to DPI_AWARENESS_CONTEXT_UNAWARE, but also enables
the system to automatically improve the rendering quality of text and
other GDI-based primitives when the window is displayed on a high-DPI
monitor.

Use the GetWindowDpiAwarenessContext() function to retrieve the DPI_AWARENESS_CONTEXT handle of a Window and GetThreadDpiAwarenessContext() for the DPI_AWARENESS_CONTEXT handle of the current thread. Then GetAwarenessFromDpiAwarenessContext() to retrive the DPI_AWARENESS value from the DPI_AWARENESS_CONTEXT structure.

[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);

[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetThreadDpiAwarenessContext();

[DllImport("user32.dll", SetLastError=true)]
static extern int GetAwarenessFromDpiAwarenessContext(IntPtr DPI_AWARENESS_CONTEXT);


[DllImport("user32.dll", SetLastError=true)]
static extern int SetProcessDpiAwarenessContext(DpiAwarenessContext value);

// Virtual enumeration: DPI_AWARENESS_CONTEXT is *contextual*. 
// This value is returned by GetWindowDpiAwarenessContext() or GetThreadDpiAwarenessContext()
// and finalized by GetAwarenessFromDpiAwarenessContext(). See the Docs.

enum DpiAwarenessContext
{
    Context_Undefined = 0,
    Context_Unaware = (DPI_AWARENESS_CONTEXT) -1,
    Context_SystemAware = (DPI_AWARENESS_CONTEXT) -2,
    Context_PerMonitorAware = (DPI_AWARENESS_CONTEXT) -3,
    Context_PerMonitorAwareV2 = (DPI_AWARENESS_CONTEXT) -4,
    Context_UnawareGdiScaled = (DPI_AWARENESS_CONTEXT) -5
}

Since DPI-Awareness is thread-based, these settings can be applied to a specific thread. This can be useful when redesigning a user interface to implement DPI-Awareness, to let the System scale a less important component while focusing on the more important functionalities.

SetThreadDpiAwarenessContext
(Same parameter as SetProcessDpiAwarenessContext())

Assemblyinfo.cs
If a third-party/external component, which references WPF assemblies, redefines the DPI-Awareness status of an application, this automatic behavior can be disabled, inserting a parameter in the Project’s Assemblyinfo.cs:

[assembly: System.Windows.Media.DisableDpiAwareness]

Leave a Comment