UI 自动化和屏幕缩放

注意注意

本文档的目标读者是欲使用 System.Windows.Automation 命名空间中定义的托管 UI Automation类的 .NET Framework 开发人员。有关 UI Automation的最新信息,请参见 Windows Automation API: UI Automation(Windows 自动化 API:UI 自动化)。

Windows Vista 允许用户更改dots per inch (dpi) 设置,以使屏幕上的大多数user interface (UI) 元素显示得更大。 虽然 Microsoft Windows 中早已提供了此功能,但是,在早期版本中,缩放必须由应用程序实现。 在 Windows Vista 中,对于无法自行缩放的所有应用程序,桌面窗口管理器将对其执行默认缩放。 UI 自动化客户端应用程序必须考虑到此功能。

本主题包括下列各节。

  • 在 Windows Vista 中缩放
  • 在 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 设置下都使操作系统和应用程序的行为保持一致。 例如,Cursor.Position 通常返回逻辑坐标。 如果将光标移到对话框中的元素上,则无论 dpi 设置如何都返回相同的坐标。 如果在 (100, 100) 绘制控件,则会根据逻辑坐标进行绘制,该控件在任何 dpi 设置下都占据相同的相对位置。

在 UI 自动化客户端中缩放

UI Automation API 不使用逻辑坐标。 以下方法和属性返回物理坐标或者将其用作参数。

默认情况下,在非 96 dpi 环境中运行的 UI 自动化客户端应用程序将无法从这些方法和属性中获得正确的结果。 例如,由于光标位置使用逻辑坐标,客户端只是将这些坐标传递给 FromPoint 便无法获取光标下面的元素。 此外,应用程序无法正确地将窗口放到其工作区之外。

解决方案包含两个部分。

  1. 首先,使客户端应用程序识别 dpi。 为此,在启动时请调用 Win32 函数 SetProcessDPIAware。 在托管代码中,以下声明使此函数可用。

    <System.Runtime.InteropServices.DllImport("user32.dll")> _
    Friend Shared Function SetProcessDPIAware() As Boolean
    End Function
    
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    internal static extern bool SetProcessDPIAware();
    

    此函数使整个进程识别 dpi,也就是说,不缩放属于该进程的所有窗口。 例如,在Highlighter Sample中,构成高光点矩形的四个窗口的位置使用从 UI Automation获取的物理坐标而非逻辑坐标。 如果该示例无法识别 dpi,则会在桌面上按逻辑坐标绘制高光点,从而导致非 96 dpi 环境中的位置不正确。

  2. 若要获得光标坐标,请调用 Win32 函数 GetPhysicalCursorPos。 下面的示例演示如何声明和使用此函数。

    Structure CursorPoint
        Public X As Integer
        Public Y As Integer
    End Structure
    
    <System.Runtime.InteropServices.DllImport("user32.dll")> _
    Friend Shared Function GetPhysicalCursorPos(ByRef lpPoint As CursorPoint) As Boolean
    End Function
    
    Private Function ShowUsage() As Boolean
    
        Dim cursorPos As New CursorPoint()
        Try
            Return GetPhysicalCursorPos(cursorPos)
        Catch e As EntryPointNotFoundException ' Not Windows Vista
            Return False
        End Try
    
    End Function
    
    public struct CursorPoint
    {
        public int X;
        public int Y;
    }
    
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    internal static extern bool GetPhysicalCursorPos(ref CursorPoint lpPoint);
    
    private bool ShowUsage()
    {
        CursorPoint cursorPos = new CursorPoint();
        try
        {
            return GetPhysicalCursorPos(ref cursorPos);
        }
        catch (EntryPointNotFoundException) // Not Windows Vista
        {
            return false;
        }
    }
    
警告说明警告

不要使用 Cursor.Position。该属性在缩放环境中的客户端窗口以外的行为是不确定的。

如果应用程序直接与无法识别 dpi 的应用程序进行跨进程通信,则可能要使用 Win32 函数 PhysicalToLogicalPoint 和 LogicalToPhysicalPoint 在逻辑坐标和物理坐标之间进行转换。 

请参见

任务

Highlighter Sample