【问题标题】:GetScaleFactorForMonitor value doesn't match actual scale appliedGetScaleFactorForMonitor 值与应用的实际比例不匹配
【发布时间】:2015-09-29 16:16:45
【问题描述】:

我正在通过互操作调用 Windows8.1 Surface3 平板电脑上的 GetScaleFactorForMonitor()。它以 140 的比例因子响应。我在这款平板电脑上只有一台显示器。我将此调用的标志设置为最接近窗口和主显示器,以查看是否有任何差异,并且两者都给出 140。

问题是,实际缩放比例是 150%。当我在 Windows 中查看显示设置时,它显示为 1440x2160,但本机分辨率为 960x1440(我通过调用 Screen.PrimaryScreen.Bounds 获得)。

调用 SystemInformation.PrimaryMonitorSize.Height 和 Width 时,我也得到 960x1440。

我探索了另一条尝试使用 GetDpiForMonitor() 的方法。我的想法是,如果我得到原始 DPI,然后是缩放后的 DPI,我可以进行百分比计算。

它有点工作,但我得到的原始 DPI 值似乎是有效(缩放)DPI,反之亦然。对于 x 和 y 的有效 DPI,我得到 96,对于 raw,我得到 144。我希望 raw 是一个较低的数字。

这是我的电话:

GetDpiForMonitor(MonitorFromWindow(myTextbox.TopLevelControl.Handle, MONITOR_DEFAULTTONEAREST),
                                   MONITOR_DPI_TYPE.MDT_Effective_DPI,
                                   out effectiveDPIx,
                                   out effectiveDPIy);
GetDpiForMonitor(MonitorFromWindow(myTextbox.TopLevelControl.Handle, MONITOR_DEFAULTTONEAREST),
                                   MONITOR_DPI_TYPE.MDT_Raw_DPI,
                                   out rawDPIx,
                                   out rawDPIy);

这是我正在使用的结构,它是 MSDN 上的一个镜像。

public enum MONITOR_DPI_TYPE : int
{
    MDT_Effective_DPI = 0,
    MDT_Angular_DPI = 1,
    MDT_Raw_DPI = 2,
    MDT_Default = MDT_Effective_DPI
};

【问题讨论】:

  • 我不知道为什么GetScaleFactorForMonitor 没有返回正确的比例因子。我可以重现您描述的确切行为。当我应用 150% 的比例时(测试监视器上的 DPI 为 144,而系统 DPI 为 96),GetScaleFactorForMonitor 返回 140。显然人们会期望它返回 150,因为 144/96=1.5。至于你的第二个问题,我怀疑这与你没有在你的应用程序中包含一个清单来指定你是每显示器高 DPI 兼容的事实有关。见:msdn.microsoft.com/en-us/library/windows/desktop/…
  • 我唯一能想到的是GetScaleFactorForMonitor是基于Windows Store的缩放模式,其实和桌面应用的缩放模式不同。 (请参阅blogs.technet.microsoft.com/askcore/2015/12/08/…,特别是名为“统一和扩展扩展系统”的部分。)您最好的选择是使用GetDpiForMonitor。太糟糕了,即使在最新版本的 Windows 10 中,每个显示器的缩放支持也完全被破坏了。这些天,Windows 团队只提供了失望。

标签: c# .net windows windows-8.1


【解决方案1】:

我现在意识到这已经过时了,但值得指出为什么会出现这种情况。

Cody Gray 的评论是一个很好的起点。链接在 https://blogs.technet.microsoft.com/askcore/2015/12/08/display-scaling-in-windows-10/ 在“统一和扩展的缩放系统”标题下确实表明 Windows 10 已经统一了所有 API 的缩放。

但是,链接 https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows(也来自 Cody Gray)表明 Windows 10 实际上并没有统一它,至少如果您使用的是旧 Windows 版本。

标题“UI 框架支持每监视器 DPI 缩放”显示,例如Win32 只会返回 1703 版本(创作者更新)的正确值,而 UWP 将使用统一的比例,如果使用 Windows 版本 1607+。

如果调用 GetScaleFactorForDevice(即 100、140 或 180),早期版本的 Windows 返回相同的值。

【讨论】:

    【解决方案2】:

    您为 Surface Pro 引用的数字看起来确实有误,但是当我在我的设备上运行相同的测试时,我得到的原始 DPI 为 216(这是正确的)。你确定你要的是 RAW DPI 吗?

    MDT_RAW_DPI 返回显示器的物理 dpi - 因此不同尺寸、相同分辨率的显示器会给出不同的答案。

    例如:我的主显示器的物理宽度为 23.5",水平分辨率为 3840。3840 / 23.5 = 163 dpi

    例如:surface pro 3 的物理宽度约为 10",分辨率为 2160。2160 / 10 = 216 dpi。

    这两者都符合我从在最新 Windows 10 上运行的 MDT_RAW_DPI 获得的结果(也许他们已经修复了一些问题?)

    这是我的测试程序:

    https://gist.github.com/toptensoftware/a6b8ca2cfc9ac63e7b6687968db408a2

    【讨论】:

      【解决方案3】:

      原来这取决于调用进程的dpi意识。 如果调用应用程序已将其 dpi 感知设置为 DPI_AWARENESS_UNAWAREGetScaleFactorForMonitor() 将返回正确的结果。

      如果设置了其他任何一个(DPI_AWARENESS_SYSTEM_AWAREDPI_AWARENESS_PER_MONITOR_AWARE),则结果不正确。

      另一个问题是,GetDpiForMonitorGetDpiForWindow 只有在调用进程具有 DPI_AWARENESS_PER_MONITOR_AWARE 时才能正确工作。 看: https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor

      如果您将程序定义为在 Windows 10 或更高版本上运行,则可以通过编程方式确定感知。

      #if WINVER >= 0x0A00
          DPI_AWARENESS_CONTEXT oldContext = GetThreadDpiAwarenessContext();
          DPI_AWARENESS oldDpiAwareness = GetAwarenessFromDpiAwarenessContext(oldContext);
      #endif
      

      如果没有,似乎无法为您正在运行的显示器获取正确的 DPI 设置(或缩放因子)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-10
        • 2021-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-20
        • 2017-12-08
        相关资源
        最近更新 更多