Windows 7 上的 GetWindowRect 太小

Posted

技术标签:

【中文标题】Windows 7 上的 GetWindowRect 太小【英文标题】:GetWindowRect too small on Windows 7 【发布时间】:2011-03-12 15:46:31 【问题描述】:

我要解决的实际问题是,我想自动找出窗口周围边距的大小。如果您能找到更好的方法,请务必回答,而不是这个。

为此,我决定截取测试窗口的屏幕截图并测量边距。这很简单,因为我预计任何边距都不会是亮粉色,但我承认这是一个 hack。我使用GetWindowRect (py) 获取边界框,并使用PIL 抓取屏幕截图并裁剪到边界框。问题是,虽然裁剪操作正确,但边界框是not accurate。 Windows 7“截图工具”获取correct dimensions。我该怎么做?

【问题讨论】:

【参考方案1】:

下面列出了我的第一个想法,但如果正如您所说,您确定 GetWindowRect 返回的值不正确,请参阅RESOLUTION


GetSystemMetrics(SM_CXBORDER)GetSystemMetrics(SM_CYBORDER) 有什么问题?

您使用的方法似乎是一种非常迂回的方式,如果您可以拨打GetWindowRect(),我很确定您也可以拨打GetSystemMetrics()

另一种可能性是使用GetWindowRect 获取窗口的整个边界矩形,并使用GetClientRect 获取客户端(非边界)区域的边界矩形。

应该分别为您提供(100,200),(1000,900)(112,227),(988,888) 之类的内容,您可以计算出顶部边框为227-200,底部为900-888,左侧为112-100 和右侧作为900-888 (27,12,12,12)。


分辨率:

出现了一些调查this。这是 2006 年的一个帖子,指出您可能无法从 GetWindowsRect 获得正确的值。指向我的线程指出:

Vista 下未与 WINVER=6 链接的应用程序将在此处收到一组误导性的值,这些值不考虑 Vista Aero 应用于窗口的“玻璃”像素的额外填充。即使在 Aero Basic(不带 Glass)中似乎也会发生这种情况,以保持尺寸一致性。解决方法(如果你不想设置 WINVER=6)似乎是动态绑定到 dwmapi.dll 并使用 GetProcAddress() 获取 DwmGetWindowAttribute() 函数,并使用 DWMWA_EXTENDED_FRAME_BOUNDS 参数调用它以请求正版窗口框架尺寸。

所以基本上,使用类似的东西(您可能必须使用 ctypes 从 Python 执行此操作):

RECT r;
HRESULT stat = DwmGetWindowAttribute (
    hwnd,
    DWMWA_EXTENDED_FRAME_BOUNDS,
    &r,
    sizeof(r));

这应该会给你正确的边界矩形。

【讨论】:

嗯,它返回 1。1 不是该窗口上任何边框的宽度。我很确定我想要的那个窗口的值是 8 和 30。 如何得到 8 和 30?看起来正确的框架底部的宽度似乎与侧面的宽度相同。 所以你是说客户区上方有30个像素,底部和侧面有8个像素? 第二张图片完美地展示了窗口的边框和内容(粉红色)。根据我的测量,窗口末端和中心内容末端之间的边缘在左侧、右侧和底部是 8 像素,在顶部是 30 像素。 我实际上会打印出您从 GetWindowRect 返回的信息,然后使用 HyperSnap 之类的工具来确定坐标是否正确(我们在工作中使用它,它可以轻松找到角)。在我看来,GetWindowsRect 是在撒谎(不太可能),或者 ImageGrab.grab(bbox) 在某种程度上是有缺陷的。就个人而言,我会打赌后者。一旦确定了问题所在,我们就可以考虑替代方案(可惜截图工具没有命令行界面来抓取当前窗口)。【参考方案2】:

DwmGetWindowAttribute

http://msdn.microsoft.com/en-us/library/aa969515%28VS.85%29.aspx

【讨论】:

【参考方案3】:

首先,您调用 GetClientRect 来检索客户矩形 R1, 然后调用 AdjustWindowRectEx 根据 R1 计算准确的边界。

【讨论】:

【参考方案4】:

GetWindowRect 返回正确的值,但对于窗口的显式句柄。使用GetParent function获取父窗口句柄,GetWindoWRect为你返回最大RECT或GetParent返回值为NULL。

【讨论】:

【参考方案5】:

我知道这是一个有点老的话题。但是我花了很多时间搜索,我自己经历了 ctypes 的痛苦,才让 paxdiablo 的解决方案在 Python 中工作。只是想分享 wxPython 的工作代码示例:

try:
    f = ctypes.windll.dwmapi.DwmGetWindowAttribute
except WindowsError:
    f = None
if f: # Vista & 7 stuff
    rect = ctypes.wintypes.RECT()
    DWMWA_EXTENDED_FRAME_BOUNDS = 9
    f(ctypes.wintypes.HWND(self.GetHandle()),
      ctypes.wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS),
      ctypes.byref(rect),
      ctypes.sizeof(rect)
      )
    size = (rect.right - rect.left, rect.bottom - rect.top)        
else:      
    size = self.GetSize()

【讨论】:

【参考方案6】:

Windows 7 上的 GetWindowRect 似乎不包括右侧和底部窗口框架边缘(至少在 Aero 主题 afaik 中),如果创建窗口没有 WS_SIZEBOX样式(即,你想要一个不那么大的窗口)。

问题是,WS_SIZEBOX 与 WS_THICKFRAME 相同,而在 Aero 上,无论是否可以调整大小,窗口都有粗框。但是 GetWindowRect 函数认为不可调整大小的窗口更薄。

解决办法?您可以使用 WS_SIZEBOX 创建窗口,调用 GetWindowRect,然后使用 SetWindowLongPtr(GWL_STYLE, ...) 关闭 WS_SIZEBOX,但这会在客户区域内创建一个难看的白色边框。

相反,让 WS_SIZEBOX 保持启用状态,并在响应 WM_GETMINMAXINFO 消息时简单地为 MINMAXINFO 结构中的 ptMinTrackSize 和 ptMaxTraceSize 返回相同的值。 这将使窗口无法调整大小,并且 GetWindowRect 将返回正确的数据。唯一的缺点是,当鼠标指针移到窗口框架上时,鼠标光标仍会变为调整大小的光标,但到目前为止它是较小的弊端。

【讨论】:

以上是关于Windows 7 上的 GetWindowRect 太小的主要内容,如果未能解决你的问题,请参考以下文章

Windows 7 上的节点故障

Windows 7 上的 XAMPP 1.7.4 默认为启动页面

Windows 7 上的本地主机

有啥方法可以从 Windows 7 上的 Windows 服务启动 GUI 应用程序?

Windows 7 上的 mcms 2002

Windows 7 上的 Java 7 64 位:如何切换 Java 版本