从外部窗口的 GetWindowRect 获取 DPI 感知正确的 RECT
Posted
技术标签:
【中文标题】从外部窗口的 GetWindowRect 获取 DPI 感知正确的 RECT【英文标题】:Getting an DPI aware correct RECT from GetWindowRect from a external window 【发布时间】:2011-12-25 00:10:36 【问题描述】:我正在制作应用程序 DPI Aware,但我需要对来自其他应用程序的 HWND 执行GetWindowRect
。我的问题是这在也支持 DPI 感知的应用程序上运行良好,但我如何检测 HWND
句柄是否是 DPI 虚拟化的,例如缩放所以我可以自己缩放它?或者是否有其他我错过的 API 可以让我以 DPI 感知方式从另一个进程的 HWND 获得窗口大小?
我尝试了LogicalToPhysicalPoint
,但似乎总是失败,可能是因为 HWND 不属于我的应用程序。
【问题讨论】:
我很难相信GetWindowRect
返回的值取决于其他应用程序是否被虚拟化。真的是这样吗?你要的是顶层窗口还是子窗口的矩形?你用这个矩形做什么?
其实是我自己解决的,我必须以 DWMWA_EXTENDED_FRAME_BOUNDS 作为参数调用 DwmGetWindowAttribute。是的,它的行为确实像这样,很容易复制,创建一个 DPI 感知应用程序,将您的 DPI 设置为 >144 并尝试它。我想在某种意义上这样做是有意义的,因为虚拟化的窗口没有意识到它正在被虚拟化,并且大多数值来自窗口所属进程的上下文。
我认为这个问题应该重新打开——我不同意关闭的原因。截至目前,戴尔开始出货具有高 dpi 显示器的新戴尔 XPS 机器。这使得这个问题与许多在 Windows 上编写代码的开发人员相关。一旦您开始在 Windows 上使用高 dpi 显示器,大多数应用程序看起来都很糟糕。社区对这一地区的投入越多,我们就能越快通过这个新的雷区。
【参考方案1】:
DPI 感知标志设置在应用程序级别而不是窗口级别因此,如果您能够获取其他应用程序的特定窗口句柄的进程,那么您可以使用 GetProcessDpiAwareness()
函数来获取该特定的 dpi 感知标志过程请看这个微软文档https://msdn.microsoft.com/en-us/library/windows/desktop/dn302113(v=vs.85).aspx
【讨论】:
【参考方案2】:这不是一个实际问题。如果您将您的流程标记为高 DPI 感知,那么系统将不再进行任何类型的 DPI 虚拟化,并且 API 将不再对您谎报实际值。
特别是,如果您从支持高 DPI 的应用程序调用 GetWindowRect
或 GetClientRect
,您将获得屏幕坐标中的实际值。这不仅适用于属于您的应用程序进程的窗口,而且适用于属于其他进程的窗口,无论其他进程的 DPI 感知设置如何。
从 Windows 8.1 开始,PhysicalToLogicalPoint
和 LogicalToPhysicalPoint
函数不再是必需的,并且实际上不执行任何操作。这两个函数的文档明确指出了这一点:
在 Windows 8.1 中,额外的系统虚拟化和进程间通信意味着对于大多数应用程序,您不需要这些 API。因此,在 Windows 8.1 中,
PhysicalToLogicalPoint
和LogicalToPhysicalPoint
不再转换点。系统在自己的坐标空间中将所有点返回给应用程序。
最后一句话只是我上面所说的措辞不同的方式。 系统根据调用者的 DPI 感知返回值。 如果您的进程是高 DPI 感知的,那么您将获得真正的值。您不需要自己缩放值。如果您不了解高 DPI,那么您可能会被谎报实际值。但这是有道理的,因为假设您无法处理事实并且不会做出适当的反应。
为了清楚起见,我应该指出,现在实际上有两个级别的高 DPI 意识,从 Windows 8.1 开始(在 Windows 10 中继续):
第一个级别是早在 Windows Vista 中引入的,即高 DPI 意识。这由应用程序清单文件中的true
设置指示,它仅表示您(应用程序)能够处理设置为经典以外的系统 DPI默认为 96 DPI。
基于以上知识,我们知道如果一个具有这种DPI感知设置的进程调用一个返回屏幕坐标的API函数,它将接收到系统DPI的值。
然后是 Windows 8.1 引入的新级别,即 每显示器 高 DPI 感知。这由应用程序清单中的True/PM
设置指示,这意味着您(应用程序)能够处理具有不同 DPI 设置的不同监视器。换句话说,虽然仍然有系统默认 DPI(可能是 96 DPI 或其他),但可能有连接到系统的显示器使用不同的 DPI 设置(系统 DPI 以外的其他设置)。
再一次,基于上述理解,我们知道如果一个具有per-monitor high-DPI感知的进程调用一个返回屏幕坐标的API函数,它将接收到相对于包含的显示器的DPI的实际坐标有问题的窗口。
如果您的进程完全不支持 DPI(清单中没有设置,或 false
),那么当您调用返回屏幕坐标的 API 函数时,您将收到基于系统范围缩放/虚拟化的坐标DPI 为 96 DPI。
【讨论】:
“您将收到基于 96 DPI 的系统范围 DPI 缩放/虚拟化的坐标。” -- 或者系统 dpi 设置的任何值。 我认为你是对的,系统根据调用者的 DPI 意识返回值。但是,如果您使用 dpiAware=true,当有多个屏幕时,您将无法获得实际值。在第一种情况下,Windows 仍会缩放非主屏幕以匹配主屏幕。您需要 dpiAware=true/PM。以上是关于从外部窗口的 GetWindowRect 获取 DPI 感知正确的 RECT的主要内容,如果未能解决你的问题,请参考以下文章
WinApi中的GetClientRect和GetWindowRect有啥区别?