了解屏幕缩放问题
Windows Vista 和更高版本的操作系统使用户能够更改每英寸点数 (dpi) 设置,以便屏幕上的大多数 UI 元素显示得更大。 在早期版本的 Windows 中,缩放必须由应用程序实现。 在 Windows Vista 及更高版本中,桌面窗口管理器 (DWM) 对所有不处理自身缩放的应用程序执行默认缩放。 Microsoft UI 自动化客户端应用程序必须考虑此功能。
本主题包含以下各节:
Windows Vista 及更高版本中的缩放
默认 dpi 设置为 96,这意味着 96 像素占名义上一英寸的宽度或高度。 “一英寸”的确切度量取决于监视器的大小和物理分辨率。 例如,在 12 英寸宽、水平分辨率为 1280 像素的监视器上,96 像素的水平线将扩展大约 9/10 英寸。
更改 dpi 设置与更改屏幕分辨率不同。 通过 dpi 缩放,屏幕上的物理像素数保持不变。 但是,缩放将应用于 UI 元素的大小和位置。 对于桌面和未显式要求不缩放的应用程序,DWM 可以自动执行此缩放。
实际上,当用户将缩放比例设置为 120 dpi 时,屏幕上的垂直或水平英寸将增大 25%。 所有尺寸会相应缩放。 应用程序窗口与屏幕上边缘和左边缘的偏移量增加 25%。 如果启用了应用程序缩放,且应用程序为非 dpi 感知应用程序,则窗口的大小按与其包含的所有 UI 元素的偏移量和大小相同的比例增大。
备注
默认情况下,当用户将 dpi 设置为 120 时,DWM 不会对非 dpi 感知应用程序执行缩放,而是在 dpi 设置为 144 或更高的自定义值时执行缩放。 但是,用户可以重写默认行为。
屏幕缩放为以任何方式与屏幕坐标有关的应用程序带来了新的挑战。 屏幕现在包含两个坐标系:物理和逻辑。 点的物理坐标是与原点左上角的实际偏移量(以像素为单位)。 如果像素本身进行了缩放,则逻辑坐标为缩放后的偏移量。
假定你设计了一个对话框,其按钮位于坐标 (100, 48) 处。 当此对话框按默认值 96 dpi 显示时,该按钮所处的物理坐标为 (100, 48)。 当为 120 dpi 时,它所处的物理坐标为 (125, 60)。 但逻辑坐标与 dpi 设置相同:(100, 48)。
逻辑坐标很重要,因为它们使操作系统和应用程序的行为保持一致,而不考虑 dpi 设置。 例如, GetCursorPos 函数通常返回逻辑坐标。 如果将光标移到对话框中的元素上,则无论 dpi 设置如何,都会返回相同的坐标。 如果你在 (100, 100) 处拖动控件,它将被拖到这些逻辑坐标,并将占用与任何 dpi 设置相同的相对位置。
UI 自动化客户端中的缩放
UI 自动化 API 不使用逻辑坐标。 以下方法和属性返回物理坐标或采用物理坐标作为参数。
- IUIAutomation::ElementFromPoint
- IUIAutomation::ElementFromPointBuildCache
- IUIAutomationElement::GetClickablePoint
- IUIAutomationElement::CurrentBoundingRectangle
- IUIAutomationElement::CachedBoundingRectangle
- IRawElementProviderFragment::BoundingRectangle
默认情况下,在未设置为 96 dpi 的环境中运行的UI 自动化应用程序将不会从这些方法和属性获取正确的结果。 例如,由于光标位置位于逻辑坐标中,因此客户端无法将这些坐标传递给 IUIAutomation::ElementFromPoint 以获取光标下的元素。 此外,该应用程序不能正确将 Windows 放置在其客户端区域之外。
该解决方案包含两个部分。
首先,使客户端应用程序成为 dpi 感知的应用程序。 为此,请在启动时调用 SetProcessDPIAware 函数。 此函数使得整个进程成为 dpi 感知的进程,这意味着属于该进程的所有窗口都不会被缩放。
其次,若要获取光标坐标,请调用 GetPhysicalCursorPos 函数。