DPI意识 – 在一个版本中不知道,在其他版本中意识到系统

所以我们有这个奇怪的问题。 我们的应用程序是一个C#/ WinForms应用程序。 在我们的6.0版本中,我们的应用程序不支持DPI。 在我们的6.1版本中,它突然变成DPI意识。 在6.0版本中,如果您在高DPI中运行它,它使用Windows位图缩放,这很好,因为这不会影响屏幕布局。 在6.1版本中,因为它由于某种原因变得DPI意识到,用户界面被破坏了。 我们现在无法解决这个问题。 我们有数百个屏幕,因此在DPI识别模式下使它们全部正常工作将花费大量时间。

我们已使用SysInternals Process Explorer确认了这一点。 在我们的6.0版本中,它显示了Unaware ,但在我们的6.1版本中,它最初显示Unaware ,但随后更改为System Aware 。 后者发生在代码从EXE进入包含所有用户界面代码的程序集DLL中时(我们的EXE基本上是一个非常薄的shell;它实际上只是在我们的表示层程序集上调用一个Controller类。)

我们已确认以下内容:

  • 两个版本都使用VSS 2017在发布模式下构建。
  • 两个版本都针对相同的.NET Framework(4.5)
  • 两个版本都使用相同的DevExpress版本。
  • 两个版本都具有相同的应用程序清单,该清单启用DPI感知设置。
  • 这两个版本都没有任何与DPI相关的Windows API的调用。
  • 使用Sys Internals和一些消息框,我们确定了6.1版本在什么时候开始意识到(演示程序集中的入口点)以及那时加载了什么DLL(我们的,DevExpress,其他依赖项),然后我们构建了一个小的虚拟应用程序,引用相同的DLL,并确认它们已加载。 该虚拟应用程序不会成为DPI意识。
  • 我们比较了两个版本之间的主要csproj文件,没有任何有意义的差异。
    • 两个版本都没有引用WPF中的任何内容。

我们不明白为什么我们的6.1版本突然变得DPI意识到了。 我们还无能为力,我们需要一个修复程序,将此版本恢复到DPI unaware模式。 它正在阻止我们的发布。 非常感谢任何指针。 我们愿意在这一点上尝试任何事情。

关于本课题报告的问题
一个应用程序,DPI不知道设计,依靠Windows虚拟化来扩展其UI内容,突然(虽然经过一些修改,导致次要版本更新) – 显然没有可观察到的原因 – 成为DPI-Aware(系统意识) )。

  • 该应用程序还依赖于app.manifest 的解释,其中缺少DPI感知定义,默认(对于向后兼容性)DPI-Unaware。

  • 没有对WPF程序集的直接引用,也没有与DPI相关的API调用。

  • 该应用程序包括第三方组件(可能还包括外部依赖项)。


由于DPI-Awareness已经成为UI呈现的一个相关方面,考虑到可用屏幕分辨率的多样性(以及相关的DPI缩放设置),大多数组件生产商已经适应了高DPI,他们的产品是DPI-Aware(DPI更改时的扩展)检测到并使用DPI-Aware程序集(通常根据定义引用WPF程序集,DPI-Aware)。

当其中一个DPI-Aware组件在项目中(直接或间接)被重新启用时,DPI-Unaware应用程序将在DPI-Awareness未明确禁用时成为DPI-Aware。

声明程序集DPI-Awareness的更直接(和推荐)方法是在应用程序清单中明确声明它。

有关Visual Studio 2017之前的应用程序清单设置,请参阅Hans Passant答案:
如何配置应用程序以在具有高DPI设置的计算机上运行

在Visual Studio 2015-Upd.1和Visual Studio 2017 app.manifest ,此设置已存在,只需要取消注释即可。 设置部分: false

    //(...)    false   //(...)  

有关更多信息,请参阅这些MSDN文章:
Windows上的高DPI桌面应用程序开发
设置进程的默认DPI感知

另一种方法是使用以下Windows API函数设置进程上下文DPI-Awareness:

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,版本1703
SetProcessDpiAwarenessContext()
(当选择Per-Monitor DPI-Awareness时,请使用Context_PerMonitorAwareV2

 [DllImport("user32.dll")] static extern int SetProcessDpiAwarenessContext(ContextDPIAwareness value); enum ContextDPIAwareness { Context_Unaware = -1, Context_SystemAware = -2, Context_PerMonitorAware = -3 Context_PerMonitorAwareV2 = -4 } 

由于DPI-Awareness是基于线程的,因此这些设置可以应用于特定线程。 在重新设计用户界面以实现DPI-Awareness时,这可能很有用,让系统在关注更重要的function时缩放不太重要的组件。

SetThreadDpiAwarenessContext
(与SetProcessDpiAwarenessContext()相同的参数)

Assemblyinfo.cs
如果引用WPF程序集的第三方/外部组件重新定义了应用程序的DPI-Awareness状态,则可以禁用此自动行为,在Project Assemblyinfo.cs插入参数:

 [assembly: System.Windows.Media.DisableDpiAwareness]