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

我试图在WinForms应用程序中检测是否由于操作系统具有高DPI而以缩放/虚拟化模式启动.目前,在运行速度为3840×2400且系统缩放为200%的系统中,应用程序将分辨率视为1920×1200,DPI视为96,比例因子为1.

我们正在使应用程序支持DPI,但在此之前,我们需要一个“快速修复”,以便我们检测是否缩放.这样做的原因是它破坏了截取屏幕截图的应用程序中的功能.我们使用Graphics.CopyFromScreen中的缩放尺寸,它采用了错误尺寸的屏幕截图,因为它需要非缩放尺寸.

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

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

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

但是要回答你的实际问题:如果你绝对需要突破操作系统,那就是在一个不支持DPI的过程中,我可以想到两种方法:

>调用GetScaleFactorForMonitor功能.如果生成的DEVICE_SCALE_FACTOR值不是SCALE_100_PERCENT,则缩放.如果您的应用程序不支持DPI,那么您正在进行虚拟化.

这是一个快速而肮脏的解决方案,因为只需要从WinForms应用程序调用它就可以获得简单的P / Invoke定义.但是,你不应该依赖它的结果而不是布尔“我们是否缩放/虚拟化?”指示符.换句话说,不要相信它返回的比例因子!

在系统DPI为96且高DPI监视器具有144 DPI(150%缩放)的Windows 10系统上,GetScaleFactorForMonitor函数在预期返回SCALE_150_PERCENT(144/96 == 1.5)时返回SCALE_140_PERCENT.我真的不明白为什么会这样.我唯一可以理解的是它是为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根据左,上,右和底部区域表示矩形.宽度是右侧范围减去左侧范围,而高度是底部范围减去顶部范围. )

由GetMonitorInfo填充的MONITORINFOEX结构也将为您提供该监视器的名称(szDevice成员).然后,您可以使用该名称来调用EnumDisplaySettings函数,该函数将填充DEVMODE结构,其中包含有关该监视器的物理显示模式的大量信息.您感兴趣的成员是dmPelsWidth和dmPelsHeight,它们分别为您提供每个宽度和高度的物理像素数.

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

示例代码,在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感知应用程序 - 代码日志