问题描述
using System;
using System.Drawing;
using System.Windows.Forms;
class HelloWorld: Form
{
public static void Main()
{
Application.Run(new HelloWorld());
}
public HelloWorld()
{
Text = "Hello World";
BackColor = Color.White;
}
protected override void OnPaint(PaintEventArgs pea)
{
Graphics grfx = pea.Graphics;
this.Width = 200;
this.Height = 100;
grfx.DrawString(
$"({this.Width},{this.Height}) Hello,Windows Forms!",Font,Brushes.Black,0);
base.OnPaint(pea);
}
}
== 案例 1.0 ==
在默认的 Windows 7 系统上运行时。它是这样显示的,没什么特别的:
窗口宽度和高度分别为 200 和 100。
== 案例 1.5 ==
当我将系统 DPI 缩放比例设置为 150%(并重新启动操作系统)时,C# 程序仍然像以前一样报告窗口宽度和高度,但是,窗口的实际屏幕大小(像素数)变为 300 和 150。这意味着, Windows 系统在我的窗口上应用了所谓的位图缩放。
那么我的问题是:我怎么知道(在我自己的程序中,或者使用 HWND 句柄),系统是否对窗口应用了位图缩放,以及缩放因子是多少。
在 Window 10 上,事情变得更加复杂,因为根据 HWND 的当前驻留监视器,Windows 10 可能会对不同的 HWND 应用不同的缩放因子。
我希望得到一个适用于 Windows 7 和 10 的答案。
解决方法
经过一个月的调查,我发现这是一个非常复杂的问题。在 Windows 7、Windows 8.1 和 Win10.1607+ 上的情况都不同。
最后,我设法编写了一个演示程序(基于一个开源项目),可以由系统确定位图拉伸因子。
说话很便宜,所以这是我的工作代码: https://github.com/chjfth/window-finder-control
特点:
- 在 Windows 7 和 Win10.1607+ 上,无论调用方程序本身是 DPI-unaware、SysDpi-aware 还是 Per-monitor-aware,我都能准确地分辨出来。
- 在 Windows 8.1 ~ Win10.1511 上,调用程序必须每个监视器都可以准确地告诉它。这是因为缺乏对 Win81 的基本混合模式 DPI 缩放的 API 支持。
在下面查看我的演示 UI
图 1:
说明:
-
WinRect 通过调用
GetWindowRect
显示 RECT 值,即所谓的虚拟 RECT 大小。 - 左上角的红色数字显示了调用程序“计算或推导出”的目标窗口的物理尺寸。
它们都是 400x241,所以目标窗口没有被位图拉伸。
图 2:在全局 DPI 缩放设置为 150% 的 Windows 7 上,物理 RECT 比虚拟 RECT 大 150%(按宽度或高度比较,宽度为 600 与 400)。因此,目标窗口被拉伸了 150%。
图 3:在 Win10.1909 上,我们还可以看到 150% 位图拉伸的窗口。
图 4:在 Win10.1909 上,我们甚至可以看到位图缩小的 HWND。当调用者是 SysDpi-aware(150%) 并且目标窗口也是 SysDpi-aware 但驻留在 100%-scaling 监视器上时,就会发生这种情况。