对 Windows Vista 和 Windows 7 中的 DPI 虚拟化和 DPI 感知应用程序进行故障排除

发布于 2024-10-18 23:02:41 字数 2297 浏览 8 评论 0原文

我遇到一个问题,应用程序(用 Delphi 编写)在所有系统上的默认 96 DPI 设置下表现正常,但在不同系统上的“150% 文本大小”(内部 144 dpi)设置下表现不一致。似乎在某些系统上,我的应用程序的某些文本/字体部分正在被拉伸,而在其他系统上,它们则没有。我本以为我的应用程序在特定版本的 Windows (Win7) 上、在特定的 DPI 下应该具有相同的行为方式。

我的应用程序要么让 Windows 知道它不需要 DPI 虚拟化功能,要么就不需要。我就明白这么多了。我不明白的是 DPI 的变化如何导致两台机器上的外观不同,两台机器都运行 Windows 7,分辨率均为 144 dpi,以相同的固定尺寸显示相同的字体和表单。

DPI 虚拟化中是否存在一些我需要在 Windows 中检查的依赖于配置的元素(注册表等)?否则,您如何排除故障并了解 DPI 虚拟化是否正在客户端窗口上完成?

在 Delphi 中,如果不想缩放,则必须将 TForm.Scaled 属性设置为 false。但我不明白的是,当主窗体的 Scaled 属性为 true 时,我不能总是预测结果。

在我的应用程序中,最让我困惑的是,我有一个控件,它只在我的大型实际应用程序中表现不佳,但在我试图调试该控件的独立应用程序中却不会表现不佳。为了了解独立应用程序中的控制行为,我被迫制作一个演示应用程序,在其中通过清单文件强制 DPI 感知。然后我可以重现控制绘图故障,尽管形式不同。

这是我在演示应用程序中使用的清单文件,它暴露了我的控件在处理 Windows 中的高 dpi 设置时遇到的问题。 可能会

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
  <assemblyIdentity version="14.0.3615.26342" processorArchitecture="*"            
   name="TestProject" type="win32"></assemblyIdentity>
  <description>High DPI Controls Test App</description>
</assembly>

然而,我发现的一件奇怪的事情是,当我在应用程序中禁用 DPI 虚拟化时,应用程序中的控件 出现混乱,这里是大约 30 个地方之一的示例。通过关闭我表单中的 Scaled 属性解决了这个特殊的问题。但在其他地方, TForm.Scaled=false 会导致问题,而在某些形式中,它会修复它:

DPI Glitch Example with一个非 DPI 虚拟化应用程序清单,但主窗体是 Scaled

更新:事实证明,我的一些控件使用 GDI+,并且 GDI+ 上下文中的字体行为与正常 GDI 上下文中的字体行为不同,至少对于某些第三方- 使用 GDI+ 的派对控件。这是令人头痛的一个主要原因。其次,VCL 中对于 DPI 感知的测试覆盖率参差不齐,且要求定义不明确。一些 VCL 控件基于 MS 通用控件,虽然可以公平地说,底层通用控件可能在高 DPI 情况下工作正常,但并非所有 VCL 控件包装器都能保证正确工作。因此,检查应用程序的所有控件是否具有高 DPI 意识,以及所有可用 Windows 7 主题中的正确行为:

  1. 航空玻璃打开,96dpi(大多数现代硬件上的默认 Win7 外观)
  2. 基本主题(航空玻璃关闭)但是xp主题启用了
  3. 经典的win2000外观(玻璃关闭),以及xp级别主题,
  4. 高对比度白色
  5. 高对比度黑色
  6. 各种其他96-DPI设置

..这样的例子不胜枚举.,你的负担相当沉重,作为应用程序开发人员。无论您是使用 VCL 的 delphi 用户,还是 MFC/ATL C++ 开发人员,在我看来,支持所有各种古怪的 Windows 模式都是一个几乎难以承受的负担。所以大多数人都不会打扰。我说得对吗?

I have a problem where an application (written in Delphi) is behaving properly at default 96 DPI settings on all systems, but is behaving inconsistently at the "150% text size" (internally 144 dpi) setting, on different systems. It seems that on some systems, that certain text/font parts of my application are being stretched and on other systems, they aren't. I would have thought that my application, on a certain version of Windows (Win7), at a certain DPI, should behave the same way.

Either my application will make it known to Windows that it doesn't need the DPI Virtualization feature, or it will not. That much I understand. What I don't understand is how DPI changes can cause different appearance on two machines, both running Windows 7, both at 144 dpi, displaying the same fonts and forms at the same fixed sizes.

Is there some configuration-dependant elements involved in DPI Virtualization that I need to inspect in windows (registry etc)? Otherwise, how do you troubleshoot and know whether DPI virtualization is being done on your client window?

In Delphi, one has to set the TForm.Scaled property to false, if one doesn't want scaling. But what I don't understand is that when the main form's Scaled property is true, I cannot always predict the outcome.

What is most perplexing to me in my application, is that I have a control that only misbehaves in my large real application, but which does not misbehave in a standalone app where I am trying to debug just the control. To understand the control behaviour in a standalone app I was forced to make a demo app where I force the DPI awareness in via the manifest file. Then I can reproduce the control drawing glitch, although in a different form.

Here is the manifest file I use in my demo app, which exposes the problems that my controls have with dealing with high-dpi settings in windows. However, one strange thing I have found is that it is possible for an application

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
  <assemblyIdentity version="14.0.3615.26342" processorArchitecture="*"            
   name="TestProject" type="win32"></assemblyIdentity>
  <description>High DPI Controls Test App</description>
</assembly>

here's an example of one of about 30 places where the controls in my application are messed up when I disable DPI virtualization in my app. This particular glitch was solved by turning off the Scaled property in my form. But in other places, having TForm.Scaled=false causes the problem, whereas in some forms, it fixes it:

DPI Glitch Example with a non-DPI-virtualized app manifest, but main form is Scaled

Update: It turns out that some of my controls use GDI+ and that font behaviour in GDI+ contexts is different than font behaviour in normal GDI contexts, at least for certain third-party controls that use GDI+. That's a major source of headaches. Secondly, there is the spotty test coverage and poorly-defined requirements, for DPI awareness, in the VCL. Some VCL controls are based on MS Common Controls, and while it's fair to say that the underlying common controls probably work fine in high-DPI situations, not all the VCL control wrappers can be guaranteed to work correctly. So, reviewing an application for high-DPI-awareness in all its controls, as well as correct behaviour in all available windows 7 themes:

  1. aero glass on, at 96dpi (Default Win7 appearance on most modern hardware)
  2. basic theme (aero glass off) but xp themes enabled
  3. classic win2000 look where glass is off, as well as xp level themes,
  4. high contrast white
  5. high contrast black
  6. Various Other-than-96-DPI settings

..and the list goes on., and you have a pretty heavy burden, as an application developer. Whether you are a delphi user and use the VCL, or you are an MFC/ATL C++ developer, it seems to me, that supporting all of the various quirky windows modes is a burden almost too heavy to bear. So most people don't bother. Am I right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

南街九尾狐 2024-10-25 23:02:41

您需要通过如下部分来表明您的应用程序支持 DPI:

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

如果您这样做,那么您将无法获得 DPI 虚拟化。

你不应该使用 DPI 虚拟化,所以我认为尝试弄清楚它是如何工作的没有什么意义。它很容易依赖于显卡驱动程序。我们几乎不可能解释为什么虚拟化会这样:您甚至没有提供任何屏幕截图、硬件详细信息等。但是,您根本不应该费心去诊断这一点。显示为 dpiaware,这不是问题。

作为参考,我为您提供:

You need to manifest that your app is DPI aware with a section like this:

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

If you do this then you won't get DPI virtualization.

You aren't meant to use DPI virtualization so I think there's little point trying to work out how it works. It could easily be dependent on graphics card drivers. It's almost impossible for us to explain why virtualization is behaving this way: you haven't even given any screenshots, hardware details etc. However, you simply shouldn't bother trying to diagnose this. Manifest as dpiaware and it's a non-problem.

For reference I offer you:

无戏配角 2024-10-25 23:02:41

这实际上是一个不同的问题。

您的表单不应该随着用户的DPI而变大,而应该随着字体大小而变大。

Delphi 的默认表单字体8pt Tahoma
平均 8pt Tahoma 字符为:6.08px * 13px

从 Windows Vista 开始,默认字体为 9pt Segoe UI
平均 9pt Segoe UI 字符为:6.81px * 15 像素。

在此处输入图像描述

您的 Windows 应用程序应该尊重用户的字体首选项(即 IconTitleFont)。

我的 Windows 字体首选项是 12pt Segoe UI,其平均字符大小为:8.98px * 21px:

在此处输入图像描述

这意味着如果您在 Tahoma 8pt您的表单>(13px 高),您需要将窗体和所有子控件缩放 162%:

scaleFactor = (userFontSize / designedFontSize)
            = 21px / 13px
            = 1.615

如果您细心,您会注意到更改 DPI 只是更改字体大小的一种特殊情况。您的 8pt 字体仍然是 8pt,但 8pt 会转化为更多像素。如果您运行 131dpi(Windows 7 中的 136% 缩放设置),则:

9pt Segoe UI,  96dpi = 6.81px x 15px
9pt Segoe UI, 131dpi = 8.98px x 21px

在此处输入图像描述

注意:我选择 131dpi12pt 作为示例并非巧合。在工作中,我运行 96dpi 但 12pt。在家里我运行 9pt 但 131dpi。两者具有相同的像素字体高度,21px。


最后,您需要根据大小差异调用 ScaleControl:

procedure StandardizeFormFont(Form: TForm);    
var
   iconTitleFontName: string;
   iconTitleFontSizePixels: Integer;
   currentFontSizePixels: Integer;
begin
   GetIconTitleFont(iconTitleFontName, iconTitleFontSizePixels);

   //Change font face first
   //Some fonts are inheriently taller than others
   //(even at the same point size)
   Form.Font.Name := iconTitleFontName;     

   //Get the form's current font height (in pixels)
   currentFontSizePixels := Form.Font.Height; //negative number, but so is iconTitleFontSizePixels

   Form.ScaleBy(iconTitleFontSizePixels, currentFontSizePixels);
end;

实际上,这段代码非常简单。许多子控件需要手动更新:

  • 列表视图列需要
  • 使用 ParentFont = false 获得更宽的控件,需要调整其字体,
  • TImage 控件需要将其图像拉伸到新尺寸,
  • 工具栏需要使用更大的图像

实际上,我们使用 StandardizeFormFont 的超级版本,它递归地遍历表单上的所有控件,并最好根据每个控件的内容调整每个控件。

Delphi 的每个控件都应该重写它的 ScaleControl 方法,并进行所需的调整:

protected
   procedure ChangeScale(M, D: Integer); override;

It's actually a different question than that.

Your forms should not be getting larger with the user's DPI, they should be getting larger with font size.

Delphi's default form font is 8pt Tahoma.
The average 8pt Tahoma character is: 6.08px * 13px.

Starting with Windows Vista, the default font is 9pt Segoe UI.
The average 9pt Segoe UI character is: 6.81px * 15px.

enter image description here

Your Windows applications should be honoring the user's font preference (i.e. IconTitleFont).

My Windows font preference is 12pt Segoe UI, whose average character size is: 8.98px * 21px:

enter image description here

This means that if you designed your form at Tahoma 8pt (13px high), you need to scale the form, and all the child controls, by 162%:

scaleFactor = (userFontSize / designedFontSize)
            = 21px / 13px
            = 1.615

If you're careful you'll notice that changing DPI is just a special case of changing the font size. Your 8pt font is still 8pt, but 8pt translates into more pixels. If you run 131dpi (136% zoom setting in Windows 7) then:

9pt Segoe UI,  96dpi = 6.81px x 15px
9pt Segoe UI, 131dpi = 8.98px x 21px

enter image description here

Note: It's not a coincidence that i chose 131dpi and 12pt as my examples. At work i run 96dpi but 12pt. At home i run 9pt but 131dpi. Both have the same pixel font height, 21px.


In the end you need to call ScaleControl by the size difference:

procedure StandardizeFormFont(Form: TForm);    
var
   iconTitleFontName: string;
   iconTitleFontSizePixels: Integer;
   currentFontSizePixels: Integer;
begin
   GetIconTitleFont(iconTitleFontName, iconTitleFontSizePixels);

   //Change font face first
   //Some fonts are inheriently taller than others
   //(even at the same point size)
   Form.Font.Name := iconTitleFontName;     

   //Get the form's current font height (in pixels)
   currentFontSizePixels := Form.Font.Height; //negative number, but so is iconTitleFontSizePixels

   Form.ScaleBy(iconTitleFontSizePixels, currentFontSizePixels);
end;

In reality this code is very simplistic. Many child controls need to be updated manually:

  • listview columns need to get wider
  • controls with ParentFont = false need to have their font's adjusted
  • TImage controls need to stretch their image to the new size
  • toolbar's need to use larger images

In reality we use a super-fancy version of StandardizeFormFont that recursively goes through all controls on the form and does it's best to adjust each control based on what it is.

Every control is Delphi is supposed to override it's ScaleControl method, and make the adjustments it needs to:

protected
   procedure ChangeScale(M, D: Integer); override;
淡水深流 2024-10-25 23:02:41

在此处输入图像描述“自定义 DPI 设置”窗口具有“使用 Windows XP 样式 DPI 缩放”。这可能可以解释不同的行为。

enter image description hereThe "Custom DPI Setting" window has a "Use Windows XP style DPI scaling". That might explain the different behaviour.

初熏 2024-10-25 23:02:41

事实证明,当系统 DPI 更改为默认的 96 dpi 值时,我的应用程序中的怪癖主要分为三个阵营:

  1. 应用程序的某些控件使用 GDI,某些控件使用 GDI+。 GDI 和 GDI+ 字体在不同 DPI 下的呈现方式存在一些差异,至少在我使用的控件中是这样。

  2. 我在delphi中使用一个名为VCL的框架。在这个Delphi VCL框架中,有些表单TForm.Scaled=true,有些表单TForm.Scaled=false。因为它要求您以缩放形式考虑每个控件,所以作为 UI 设计师,您经常会发现缩放形式“丑陋”或不可接受。关闭“缩放”后,您留下的表单要么被 Windows 7 本身在高 DPI 设置(DPI 虚拟化模式)下拉伸,要么显得很小,因此,如果您愿意,可以忽略用户的“请求”,以获得 144 dpi 版本的表单您的 96 dpi 用户界面。其他人可能正在使用其他语言的其他框架,或者甚至可能使用非常老式的东西,例如 Visual C++ 的对话框编辑器,您可以在“对话框单元”中设计对话框,这是尝试分离常规对话框的另一种方法布局,从 1:1 对应到像素。缩放、拉伸和布局控件是 UI 设计的常见部分,必须以符合平台要求的方式来解决。就我而言,VCL 做得很好,让我设计了一个支持 96 DPI 的玻璃航空应用程序,并且在其他 DPI 等级下也能很好地工作,但我的大多数自定义控件却不能。因此,坚持使用 VCL 附带的控件的另一个原因是:如果您关心高 DPI 支持,那么当您尝试使高 DPI 支持起作用时,您的工作就会变得更加困难。

  3. DPI 虚拟化又由您必须明确包含在应用程序中的清单设置控制。由于我的应用程序已经有一个自定义清单(不是当您单击项目设置中的 Enable-windows-themes 复选框时包含在 Delphi 应用程序中的清单),我能够再次打开和关闭此 DPI 虚拟化,并进行测试我在这两种情况下的申请。我发现我的应用程序在没有 DPI 虚拟化的情况下无法运行,因此我将 Windows 保留为其默认行为。其他人的应用程序可能会轻松地在禁用 DPI 虚拟化的情况下工作,如果他们使用 100% vcl 控件,并且表单使用表单缩放或其他一些技术,以在各种情况下适当调整自身大小(例如 DevExpress 的 VCL ExpressLayout 控件)。字体大小和 DPI 间距。)。在我看来,最终VCL的功能已经足够了,但对于真正具有工业强度的解决方案,需要比VCL更先进的框架,才能全面正确地处理“高DPI环境”等问题,并且在这些情况下,第三方控件的设计通常无法达到当前 VCL 的效果。这种框架问题在 WPF 框架 (Microsoft) 和 Java (wing) 中非常明显,但不是 VCL 的经典“Win16/Win32 通用控制框架”的一部分。

总而言之,这些变化与过去相比并没有那么不同(复杂),当时 Windows XP 和其他版本的 Windows 为您提供了“字体大小”的选择,而现在,Windows 7 UI 体验试图掩盖字体点大小选项非常深入,而是为您提供了一个“文本大小”更改用户界面,可以修改表面以下的系统 DPI。这两种改变用户体验的方法都会导致几乎每个用户都遇到至少一个主要商业应用程序的问题,该应用程序在所产生的更改中看起来无法正常工作。随着超高点距显示器在消费类 PC 领域变得越来越普遍,这个问题可能会变得越来越严重,并且需要围绕更合适的框架进行 UI 设计。

It turns out that the quirks in my Application when the system DPI changed away from the default 96 dpi value, are in three general camps:

  1. Some of the application's controls use GDI and some controls use GDI+. There are some differences in how a GDI and GDI+ font renders at different DPI, at least in the controls I am using.

  2. I use a framework called VCL in delphi. In this Delphi VCL framework, some forms have TForm.Scaled=true, and some have TForm.Scaled=false. Because it requires you to think about each control in a scaled form, it is very common to have things happen that you as a UI designer will find "ugly" or unacceptable in a Scaled form. Turning Scaled off, you are left with forms that are either stretched by Windows 7 itself, in high DPI settings (DPI Virtualization mode) or which appear small and therefore, ignore the user's "request" if you like, for a 144 dpi version of your 96 dpi UI. Other people might be using other framworks in other languages, or might even be using something really old fashioned, like the Dialog Box Editor for Visual C++, where you design dialogs in "Dialog Units", which is another way of trying to separate general dialog layout, from a 1:1 correspondence to pixels. Scaling, stretching, and layout controls are a general part of UI design that must be solved in a way that matches platform requirements. In my case, the VCL does a great job of letting me design a 96 DPI glass-enabled aero app, and works great at other DPI ratings, but most of my custom controls don't. So it's another reason to stick with the controls that come with the VCL: If you care about high DPI support, your job gets harder when you try to make high DPI support work.

  3. DPI Virtualization in turn, is controlled by a manifest setting that you must expressly include in your application. Since my application already had a custom manifest (not the one that is included with your Delphi app when you click the Enable-windows-themes checkbox in the project settings), I was able to turn this DPI virtualization on and off again, and test my application in both cases. I found that my application was not ready to run without DPI Virtualization, and thus I left Windows to its default behaviour. Other people's applications might easily work with DPI Virtualization disabled, if they use 100% vcl controls, with forms that either use Form scaling, or some other technique, to appropriately size themselves (such as the VCL ExpressLayout control from DevExpress) at a variety of font sizes, and DPI pitches.). It seems to me, that in the end, the VCL is functional enough, but that for really industrial-strength solutions, a more advanced framework than the VCL is required, to comprehensively deal with issues like "high DPI environments" properly, and that third party controls are generally not designed to work even as well as the current VCL works, in these cases. Such framework concerns are very much in evidence in the WPF framework (Microsoft) and in Java (wing), but are not part of the classic "Win16/Win32 common control framework" of the VCL.

All in all, these changes are not that different (complex) now, than in the old days, when Windows XP and other versions of Windows offered you a choice of "font sizes", whereas now, the Windows 7 UI experience tries to bury the font point size options pretty deeply, and instead offers you a "text size" changing user interface that modifies the system DPI below the surface. Both of these ways of changing your user experience result in almost every user having problems with at least one major commercial application that doesn't look, or work correctly with the resulting changes. As ultra-high dot-pitch displays become more common in the consumer PC landscape, this problem will probably get worse and worse, and UI design around more suitable frameworks will be required.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文