gpt4 book ai didi

c# - 检测非 DPI 感知应用程序是否已缩放/虚拟化

转载 作者:可可西里 更新时间:2023-11-01 13:01:57 24 4
gpt4 key购买 nike

我试图在 WinForms 应用程序中检测它是否由于操作系统具有高 DPI 而在缩放/虚拟化模式下启动。目前,在以 3840x2400 和 200% 缩放比例运行的系统中,应用程序看到的分辨率为 1920x1200,DPI 为 96,比例因子为 1。

我们正在使应用程序具有 DPI 感知能力,但在那之前,我们需要一个“快速修复”来检测是否缩放。这样做的原因是它破坏了应用程序中截取屏幕截图的功能。我们在 Graphics.CopyFromScreen 中使用缩放尺寸,它会截取错误尺寸的屏幕截图,因为它需要非缩放尺寸。

我知道 DPI 感知设置,但目前,我们仍然希望应用程序被缩放,但如果可能的话,能够检测到我们被缩放并获取非缩放尺寸。

最佳答案

未明确标记为高 DPI 感知的应用程序将被系统欺骗并告知有 96 DPI,缩放因子为 100%。为了获得真正的 DPI 设置,并避免 DWM 自动虚拟化,您需要包含 <dpiAware>True/PM</dpiAware>在您的应用程序 list 中。更多信息可用 here .

就您而言,听起来您正在寻找 LogicalToPhysicalPointForPerMonitorDPI PhysicalToLogicalPointForPerMonitorDPI 对函数。正如链接文档所解释的那样,默认情况下,系统将根据调用者的 DPI 感知返回有关其他窗口的信息。因此,如果非 DPI 感知应用程序尝试获取高 DPI 感知进程的窗口边界,它将获得已转换为自己的非 DPI 感知坐标空间的边界。用这些函数的术语来说,这就是“逻辑”坐标。您可以将这些转换为“物理”坐标,即操作系统(和其他高 DPI 感知进程)实际使用的坐标。

不过,要回答您的实际问题:如果您绝对需要在不了解 DPI 的进程中突破操作系统的谎言,我可以想到两种方法:

  • 调用 GetScaleFactorForMonitor 功能。 如果由此产生 DEVICE_SCALE_FACTOR value 不是 SCALE_100_PERCENT ,那么你就被缩放了。如果您的应用程序不支持 DPI,那么您正在被虚拟化。

    这是一个快速而肮脏的解决方案,因为您只需要一个简单的 P/Invoke 定义就可以从 WinForms 应用程序中调用它。但是,除了 bool 值“我们是否已缩放/虚拟化?”之外,您不应依赖其结果。指标。换句话说,不要相信它返回的比例因子!

    在系统 DPI 为 96 且高 DPI 显示器具有 144 DPI(150% 缩放)的 Windows 10 系统上,GetScaleFactorForMonitor函数返回 SCALE_140_PERCENT预计何时返回 SCALE_150_PERCENT (144/96 == 1.5)。我真的不明白为什么会这样。我唯一能弄清楚的是,它是为 Windows 8.1 上的 Metro/Modern/UWP 应用程序设计的,其中 150% 不是有效的比例因子,但 140% 是。此后缩放因子为 unified in Windows 10 ,但此功能似乎尚未更新,并且仍然为桌面应用程序返回不可靠的结果。
  • 根据显示器的逻辑和物理宽度自行计算缩放系数。

    首先,当然,您需要获得一个 HMONITOR (处理特定的物理监视器)。您可以通过拨打 MonitorFromWindow 来做到这一点。 ,将句柄传递给 WinForms 窗口,并指定 MONITOR_DEFAULTTONEAREST .这将使您了解正在显示您感兴趣的窗口的监视器。

    然后,您将使用此监视器句柄通过调用 GetMonitorInfo 来获取该监视器的逻辑宽度。功能。这会填入 MONITORINFOEX structure作为其成员之一,它包含一个 RECT包含该监视器的虚拟屏幕坐标的结构 ( rcMonitor )。 (请记住,与 .NET 不同,Windows API 用左、上、右和下范围表示矩形。宽度是右范围减去左范围,而高度是下范围减去上范围。 )
    MONITORINFOEXGetMonitorInfo 填充的结构还将为您提供该监视器的名称(szDevice 成员)。然后您可以使用该名称来调用 EnumDisplaySettings 函数,它将填写一个 DEVMODE包含有关该显示器物理显示模式的大量信息的结构。您感兴趣的成员(member)是dmPelsWidthdmPelsHeight ,分别为您提供每个宽度和高度的物理像素数。

    然后,您可以将逻辑宽度除以物理宽度以确定宽度的缩放因子。高度也是如此(除了我知道的所有显示器都有方形像素,因此垂直缩放因子将等于水平缩放因子)。

    示例代码,在 Windows 10 中测试和工作(用 C++ 编写,因为这是我手头的东西;抱歉,你必须自己翻译成 .NET):
    // Get the monitor that the window is currently displayed on
    // (where hWnd is a handle to the window of interest).
    HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);

    // Get the logical width and height of the monitor.
    MONITORINFOEX miex;
    miex.cbSize = sizeof(miex);
    GetMonitorInfo(hMonitor, &miex);
    int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
    int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);

    // Get the physical width and height of the monitor.
    DEVMODE dm;
    dm.dmSize = sizeof(dm);
    dm.dmDriverExtra = 0;
    EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
    int cxPhysical = dm.dmPelsWidth;
    int cyPhysical = dm.dmPelsHeight;

    // Calculate the scaling factor.
    double horzScale = ((double)cxPhysical / (double)cxLogical);
    double vertScale = ((double)cyPhysical / (double)cyLogical);
    ASSERT(horzScale == vertScale);
  • 关于c# - 检测非 DPI 感知应用程序是否已缩放/虚拟化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33507031/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com