c# .net winforms highdpi dpi-aware

c# - Reconocimiento de DPI-Desconocido en una versión, Sistema consciente en la otra



.net winforms (1)

Esta pregunta ya tiene una respuesta aquí:

Así que tenemos este problema realmente extraño. Nuestra aplicación es una aplicación C # / WinForms. En nuestra versión 6.0, nuestra aplicación no tiene conocimiento de DPI. En nuestra versión 6.1, de repente se ha convertido en consciente de DPI.
En la versión 6.0, si lo ejecuta con un alto DPI, usa el escalado de mapa de bits de Windows, lo cual está bien, porque eso no afecta los diseños de la pantalla. En la versión 6.1, debido a que por alguna razón se ha hecho consciente de DPI, las interfaces de usuario se dañan.
No estamos en posición de arreglar esto ahora mismo. Tenemos cientos de pantallas, por lo que hacer que todas funcionen correctamente en el modo de reconocimiento de DPI llevará mucho tiempo.

Hemos confirmado esto utilizando SysInternals Process Explorer. En nuestra versión 6.0, muestra Inconsciente , pero en nuestra versión 6.1, inicialmente muestra Inconsciente , pero luego cambia a System Aware .
Esto último ocurre cuando el código ingresa desde el EXE a nuestra DLL de ensamblaje que contiene todo nuestro código de interfaz de usuario (nuestro EXE es básicamente un shell muy delgado; todo lo que realmente hace es llamar a una clase de Controlador en nuestro ensamblaje de la capa de presentación).

Hemos confirmado lo siguiente:

  • Ambas versiones están integradas en el Modo de lanzamiento utilizando VSS 2017.
  • Ambas versiones apuntan al mismo .NET Framework (4.5)
  • Ambas versiones usan la misma versión de DevExpress.
  • Ambas versiones tienen el mismo manifiesto de aplicación, que no tiene habilitada la configuración de reconocimiento de DPI.
  • Ninguna de las versiones tiene ninguna llamada a ninguna API de Windows relacionada con DPI.
  • Usando Sys Internals y algunos cuadros de mensajes, determinamos en qué punto se reconoce la versión 6.1 (punto de entrada en el ensamblaje de Presentación) y qué DLL se cargan en ese punto (nuestro, DevExpress, otras dependencias), y luego creamos una pequeña aplicación ficticia que hace referencia a los mismos archivos DLL y confirmó que están cargados. Esa aplicación ficticia no toma conciencia de DPI.
  • Hemos comparado los principales archivos csproj entre ambas versiones y no hay diferencias significativas.
    • Ninguno de los lanzamientos hace referencia a nada de WPF.

No entendemos por qué nuestra versión 6.1 se ha convertido repentinamente en consciente de DPI. No tenemos idea de qué más ver y necesitamos una solución que haga que esta versión vuelva al modo inconsciente de DPI. Está retrasando nuestra liberación. Realmente apreciaría cualquier punteros. Estamos dispuestos a intentar cualquier cosa en este punto.


Sobre el problema reportado en esta pregunta :
Una aplicación, que no conoce DPI por su diseño, confía en la virtualización de Windows para escalar su contenido de UI, repentinamente (aunque después de algunas modificaciones, lo que lleva a una actualización menor de la versión) - y aparentemente sin una razón observable - se vuelve DPI-Aware (System Aware ).

  • La aplicación también se basa en una interpretación de app.manifest <windowsSettings> , donde la ausencia de una definición de <windowsSettings> de DPI, los valores predeterminados (para la compatibilidad con versiones anteriores) a DPI-Desconocido.

  • No hay referencias directas a los ensamblados de WPF ni llamadas a la API relacionadas con DPI.

  • La aplicación incluye componentes de terceros (y, posiblemente, dependencias externas).

Dado que DPI-Awareness se ha convertido en un aspecto relevante de la presentación de la interfaz de usuario, dada la diversidad de resoluciones de pantalla disponibles (y la configuración de escala de DPI relacionada), la mayoría de los productores de componentes se han adaptado a High-DPI y sus productos son DPI-Aware (escala cuando cambia un DPI se detecta) y hacer uso de los ensamblajes de DPI-Aware (a menudo haciendo referencia a los ensamblados de WPF, por definición de DPI-Aware).

Cuando uno de estos componentes de DPI-Aware es referenciado en un proyecto (directa o indirectamente), una aplicación que no tiene DPI se convertirá en DPI-Aware, cuando el Reconocimiento de DPI no se haya desactivado explícitamente.

El método más directo (y recomendado) para declarar un ensamblaje con conocimiento de DPI es declararlo explícitamente en el manifiesto de la aplicación.

Consulte la respuesta de Hans Passant para una configuración de manifiesto de la aplicación antes de Visual Studio 2017:
Cómo configurar una aplicación para que se ejecute en una máquina con una configuración alta de DPI

En Visual Studio 2015-Upd.1 y Visual Studio 2017 app.manifest , esta configuración ya está presente, solo debe ser sin comentarios. Establezca la sección: <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 DPIs. 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>

Consulte estos artículos de MSDN para obtener más información:
Desarrollo de aplicaciones de escritorio de alto DPI en Windows
Configuración del reconocimiento por defecto de DPI para un proceso

Otro método es establecer el contexto del proceso Reconocimiento de DPI mediante estas funciones de la API de Windows:

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, versión 1703
SetProcessDpiAwarenessContext()
(Al optar por un Context_PerMonitorAwareV2 DPI por monitor, use Context_PerMonitorAwareV2 )

Consulte también: API de escalado de DPI en modo mixto y compatibles con DPI - MSDN

Windows 10, versión 1809 (octubre de 2018)
Se ha agregado un nuevo DPI_AWARENESS_CONTEXT : DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED

DPI no es consciente de la calidad mejorada del contenido basado en GDI. Este modo se comporta de manera similar a DPI_AWARENESS_CONTEXT_UNAWARE, pero también permite que el sistema mejore automáticamente la calidad de representación del texto y otras primitivas basadas en GDI cuando la ventana se muestra en un monitor de alto DPI.

Use la función GetWindowDpiAwarenessContext() para recuperar el identificador DPI_AWARENESS_CONTEXT de una ventana y GetThreadDpiAwarenessContext() para el identificador DPI_AWARENESS_CONTEXT del hilo actual. Luego, GetAwarenessFromDpiAwarenessContext() para DPI_AWARENESS valor DPI_AWARENESS de la estructura DPI_AWARENESS_CONTEXT .

[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(InPtr DPI_AWARENESS_CONTEXT); [DllImport("user32.dll", SetLastError=true)] static extern int SetProcessDpiAwarenessContext(ContextDPIAwareness value); // Virtual enumeration: DPI_AWARENESS_CONTEXT is *contextual*. // This value is returned by GetWindowDpiAwarenessContext() or GetThreadDpiAwarenessContext() // and finalized by GetAwarenessFromDpiAwarenessContext(). See the Docs. enum ContextDPIAwareness { 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)) }

Dado que DPI-Awareness se basa en subprocesos, estas configuraciones se pueden aplicar a un subproceso específico. Esto puede ser útil cuando se rediseña una interfaz de usuario para implementar DPI-Awareness, para permitir que el sistema escale un componente menos importante mientras se enfoca en las funcionalidades más importantes.

SetThreadDpiAwarenessContext
(El mismo parámetro que SetProcessDpiAwarenessContext() )

Assemblyinfo.cs
Si un componente externo / de un tercero, que hace referencia a conjuntos de WPF, redefine el estado de Reconocimiento de DPI de una aplicación, este comportamiento automático se puede deshabilitar, insertando un parámetro en el Project Assemblyinfo.cs :

[assembly: System.Windows.Media.DisableDpiAwareness]