Windows 8.1引入了为不同监视器提供不同DPI设置的功能.此功能称为"每监视器高DPI支持".它在Windows 10中持续存在并得到进一步完善.
如果应用程序没有选择加入(即,DPI不知道或高DPI意识),它将由DWM自动扩展到正确的DPI.大多数应用程序属于这两个类别之一,包括与Windows捆绑在一起的大多数实用程序(例如,记事本).在我的测试系统上,高DPI监视器设置为150%标度(144 DPI),而普通监视器设置为系统DPI(100%标度,96 DPI).因此,当您在高DPI屏幕上打开其中一个应用程序(或将其拖到那里)时,虚拟化会启动,放大所有内容,但也会让它变得非常模糊.
另一方面,如果应用程序明确指出它支持每个监视器的高DPI,则不执行虚拟化,并且开发人员负责扩展.微软在这里有一个相当全面的解释*,但为了一个独立的问题,我将总结一下.首先,通过<dpiAware>True/PM</dpiAware>在清单中设置来指示支持.这会选择接收WM_DPICHANGED消息,它会告诉您新的DPI设置以及窗口的建议新大小和位置.它还允许您调用GetDpiForMonitor函数并获取实际 DPI,而不会因兼容性原因而被欺骗.肯尼克尔也写了一个全面的教程.
我已经在一个小小的C++测试应用程序中成功完成了所有这些工作.这是很多样板和大多数项目设置,所以我在这里发布一个完整的例子并不是很重要.如果您想测试它,请遵循Kenny的说明,MSDN上的本教程,或下载官方SDK示例.现在,客户端区域中的文本看起来很好(因为我的处理WM_DPICHANGED),但由于不再执行虚拟化,因此不会缩放非客户区域.结果是标题/标题栏和菜单栏的大小错误 - 它们在高DPI屏幕上不会变大:
所以问题是,如何让窗口的非客户区扩展到新的DPI?
无论您是创建自己的窗口类还是使用对话框,它们在这方面都具有相同的行为.
它已经表明,有是没有答案,那你唯一的选择是自定义绘制整个窗口,包括非客户区.虽然这当然是可能的,实际上UWP应用程序(以前称为Metro)的功能,例如Windows 10计算器,对于使用许多非客户端小部件并希望看起来像原生的桌面应用程序来说,它不是一个可行的选择.
除此之外,它显然是错误的.自定义绘制的标题栏不能是获得正确行为的唯一方法,因为Windows shell团队已经完成了它.简洁的"运行"对话框的行为完全符合预期,当您在具有不同DPI的监视器之间拖动时,正确调整客户端和非客户端区域的大小:
使用Spy ++进行的调查证实,这只是一个标准的Win32对话框 - 没什么特别的.所有控件都是标准的Win32 SDK控件.它不是一个UWP应用程序,也没有自定义绘制标题栏 - 它仍然具有WS_CAPTION风格.它是由explorer.exe进程,它推出被标记为每个监视器高DPI感知(使用Process Explorer和GetProcessDpiAwareness验证).此博客文章确认已在Windows 10中重写了"运行"对话框和"命令提示符"以正确扩展(请参阅" 命令shell等 ").Run对话框用于调整其标题栏的大小是什么?
负责新式打开和保存对话框的Common Item Dialog API在从每个监视器高DPI感知的进程启动时也可以正确扩展,如您在Run对话框中单击"Browse"按钮时所看到的.同样的事情任务对话框API,创建奇数情况下的应用程序启动一个对话框,以不同大小的标题栏.(但是,传统的MessageBox …
所以我们有这个奇怪的问题.我们的应用程序是一个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类.)
我们已确认以下内容:
我们不明白为什么我们的6.1版本突然变成了DPI意识.我们无知还有什么可以看,我们需要一个修复程序,将此版本恢复到DPI unaware模式.它正在阻止我们的发布.非常感谢任何指针.我们愿意在这一点上尝试任何事情.
我曾想将我的应用程序的新版本(WPF 通过 Desktop Bridge 转换为 UWP)发布到 Microsoft Store,但它未能通过认证并出现以下警告:
- 文件 **.exe 既没有在清单中显示 PerMonitorV2,也没有调用 DPI 感知 API(例如:user32!SetProcessDpiAwarenessContext 或 user32!SetThreadDpiAwarenessContext)。
- 该应用程序 *** 不支持 DPI。
我有以下问题:
首先,我在去年年底发布了我的应用程序的第一个版本,DPI 感知没有任何问题。所以这是一个新的测试?
由于这只是一个警告,所有其他测试都已通过,这可能是认证过程失败的原因吗?
所以我试图让我的应用程序 DPI 感知......
根据这个描述,我调用了SetProcessDpiAwarenessContext函数并检查了所有四个值。使用unaware和System aware标志时,我的应用程序看起来很模糊,使用Per Monitor标志时看起来不错,但是当我请求屏幕尺寸时,它返回原始宽度和高度,不计算比例(我有 150% 的比例),因此我的窗口被定位到屏幕的错误部分。问题是,如果只需要设置这 4 个标志,并且没有一个标志作为默认值(不设置任何内容),那么默认设置是什么?
好的,这里列出了 5 个标志,但它们都不起作用,就好像没有设置任何东西一样。
多谢!