c# - Sensibilisation DPI-Ignorant dans une version, Système conscient dans l'autre



.net winforms (1)

À propos du problème signalé dans cette question :
Une application, qui, de par sa conception, n’est pas consciente de la technologie DPI, s'appuie brusquement sur la virtualisation Windows pour redimensionner le contenu de son interface utilisateur (bien qu'après quelques modifications, une mise à jour mineure ait été mise à jour) - et apparemment sans raison observable - devient conforme à DPI (System Aware). ).

  • L'application s'appuie également sur une interprétation de app.manifest <windowsSettings> , dans laquelle l'absence de définition de la app.manifest DPI, est définie par défaut (pour la compatibilité en amont) sur DPI-Unaware.

  • Il n'y a pas de référence directe aux assemblys WPF et pas d'appels d'API liés à DPI.

  • L'application comprend des composants tiers (et éventuellement des dépendances externes).

Depuis que DPI-Awareness est devenu un aspect pertinent de la présentation de l'interface utilisateur, compte tenu de la diversité des résolutions d'écran disponibles (et des paramètres de redimensionnement DPI correspondants), la plupart des fabricants de composants se sont adaptés à High-DPI et leurs produits sont DPI-Aware (redimensionnement en cas de modification DPI). est détecté) et utilise des assemblages DPI-Aware (faisant souvent référence à des assemblages WPF, DPI-Aware par définition).

Lorsque l'un de ces composants DPI-Aware est référencé dans un projet (directement ou indirectement), une application DPI-Unaware deviendra DPI-Aware, lorsque DPI-Awareness n'a pas été désactivé de manière explicite.

La méthode la plus directe (et recommandée) pour déclarer un assemblage DPI-Awareness est de le déclarer explicitement dans le manifeste de l'application.

Reportez-vous à la réponse de Hans Passant pour un paramètre de manifeste d'application antérieur à Visual Studio 2017:
Comment configurer une application pour qu'elle s'exécute sur une machine avec un paramètre DPI élevé

Dans Visual Studio 2015-Upd.1 et Visual Studio 2017 app.manifest , ce paramètre est déjà présent, il doit simplement être mis en commentaire. Définissez la 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
       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>

Reportez-vous à ces articles MSDN pour plus d'informations:
Développement d'applications de bureau à haute DPI sous Windows
Définition de la sensibilité DPI par défaut pour un processus

Une autre méthode consiste à définir le contexte de processus DPI-Awareness à l'aide des fonctions API Windows suivantes:

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()
(Lorsque vous optez pour une Context_PerMonitorAwareV2 DPI par moniteur, utilisez Context_PerMonitorAwareV2 )

Voir également: API de mise à l'échelle DPI en mode mixte et DPI - MSDN

Windows 10, version 1809 (octobre 2018)
Un nouveau DPI_AWARENESS_CONTEXT a été ajouté: DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED

DPI non au courant avec une qualité améliorée du contenu basé sur GDI. Ce mode se comporte de la même manière que DPI_AWARENESS_CONTEXT_UNAWARE, mais permet également au système d’améliorer automatiquement la qualité de rendu du texte et des autres primitives basées sur GDI lorsque la fenêtre est affichée sur un moniteur à haute résolution.

Utilisez la fonction GetWindowDpiAwarenessContext() pour récupérer le DPI_AWARENESS_CONTEXT d'une fenêtre et GetThreadDpiAwarenessContext() pour le DPI_AWARENESS_CONTEXT du thread en cours. Ensuite, GetAwarenessFromDpiAwarenessContext() pour récupérer la valeur DPI_AWARENESS partir de la structure 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))
}

Étant donné que DPI-Awareness est basé sur les threads, ces paramètres peuvent être appliqués à un thread spécifique. Cela peut être utile lors de la reconception d'une interface utilisateur pour implémenter DPI-Awareness, afin de permettre au système d'adapter un composant moins important tout en se concentrant sur les fonctionnalités les plus importantes.

SetThreadDpiAwarenessContext
(Même paramètre que SetProcessDpiAwarenessContext() )

Assemblyinfo.cs
Si un composant tiers / externe, qui fait référence à des assemblys WPF, redéfinit le statut de prise de conscience en matière de DPI d'une application, ce comportement automatique peut être désactivé en insérant un paramètre dans le projet Assemblyinfo.cs :

[assembly: System.Windows.Media.DisableDpiAwareness]

Cette question a déjà une réponse ici:

Nous avons donc ce problème vraiment étrange. Notre application est une application C # / WinForms. Dans notre version 6.0, notre application n'est pas compatible DPI. Dans notre version 6.1, il est soudainement devenu conscient de DPI.
Dans la version 6.0, si vous l'exécutez avec une résolution élevée, il utilise la mise à l'échelle bitmap de Windows, ce qui est correct, car cela n'affecte pas les dispositions de l'écran. Dans la version 6.1, étant donné que, pour une raison quelconque, il est devenu conscient de la technologie DPI, les interfaces utilisateur sont endommagées.
Nous ne sommes pas en mesure de régler ce problème pour le moment. Nous avons des centaines d’écrans, il faudra donc beaucoup de temps pour les faire fonctionner correctement en mode DPI.

Nous avons confirmé cela à l'aide de SysInternals Process Explorer. Dans notre version 6.0, il est indiqué par Unaware , mais dans notre version 6.1, il est initialement indiqué par Unaware , puis modifié en fonction de l'état du système .
Cette dernière se produit lorsque le code de l'EXE entre dans notre DLL d'assembly qui contient tout le code de notre interface utilisateur (notre EXE est en fait un shell très fin; il ne fait qu'appeler une classe Controller sur notre assembly de couche de présentation.)

Nous avons confirmé ce qui suit:

  • Les deux versions sont construites en mode édition à l'aide de VSS 2017.
  • Les deux versions ciblent le même .NET Framework (4.5)
  • Les deux versions utilisent la même version de DevExpress.
  • Les deux versions ont le même manifeste d'application, pour lequel le paramètre de détection DPI n'est pas activé.
  • Aucune de ces versions ne fait appel à aucune API Windows liée à DPI.
  • À l'aide de Sys Internals et de certaines boîtes de message, nous avons déterminé à quel moment la version 6.1 devient consciente (point d'entrée dans l'assembly de présentation) et quelles DLL sont chargées à ce stade (la nôtre, DevExpress, autres dépendances), puis nous avons créé une petite application factice qui référence les mêmes DLL et a confirmé que celles-ci sont chargées. Cette application factice ne devient pas consciente de DPI.
  • Nous avons comparé les principaux fichiers csproj entre les deux versions et il n'y a pas de différence significative.
    • Aucune des deux versions ne fait référence à WPF.

Nous ne comprenons pas pourquoi notre version 6.1 est soudainement devenue consciente de DPI. Nous n'avons aucune idée de ce qu'il faut rechercher et nous avons besoin d'un correctif pour remettre cette publication en mode DPI non au courant. Il retarde notre libération. J'apprécierais vraiment tous les indicateurs. Nous sommes disposés à essayer n'importe quoi à ce stade.





dpi-aware