在Windows 7上,GetWindowRect返回的窗口大小过小

14

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

为了做到这一点,我决定对测试窗口进行截屏并测量边距。这很简单,因为我预计没有边距会是亮粉色的,但我承认这是一个hack。我使用GetWindowRect (py)来获取边框框架,然后使用PIL 来截取屏幕截图并裁剪到边框框架。问题在于,虽然裁剪操作是正确的,但边框框架是不准确的。Windows 7 的“剪切工具”获取了正确的尺寸。我应该如何做到这一点?

6个回答

19

如果像你所说的一样,GetWindowRect 返回的值是不正确的,那么请参见下面的 解决方法


"为什么不用 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)。


解决方法:

一些调查结果显示,在这个线程中提到,可能无法从 GetWindowsRect 获取正确的值。指向我这个线程的人说:

Vista 下未使用 WINVER=6 的应用程序将收到一组误导性的值,这些值不考虑 Vista Aero 应用于窗口的额外填充像素。即使在 Aero Basic(没有玻璃)中也会发生这种情况,以保持尺寸的一致性。解决方法(如果您不想设置 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。 - Devin Jeanpierre
我已经确定了谎言的来源,它是GetWindowsRect。当我看到裁剪的结果时,我手动进行了测试。这就是我说“裁剪操作正确”的原因。 - Devin Jeanpierre
澄清一下:GetWindowsRect返回的尺寸是一个宽306,高328的窗口(例如(175,175,481,503))。这与裁剪完全匹配(甚至定位正确),但比“截图工具”裁剪要小(并且切掉了角落等部分)。 - Devin Jeanpierre
@Devin,请看我的最终更新。可能是因为PIL没有与WINVER=6链接,因此可能会得到不正确的值。测试建议的解决方案是否能给您正确的值是值得的。 - paxdiablo
抱歉这么晚才回复,我一直在拖延测试,因为 ctypes 真的很麻烦。但是它完美地运行了! - Devin Jeanpierre
显示剩余5条评论

4

我知道这是一个有点老的话题。但我花了很多时间搜索,自己经历了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()

2
在Windows 7上,如果窗口在创建时没有使用WS_SIZEBOX样式(即您想要一个不可调整大小的窗口),则GetWindowRect似乎不包括右侧和下方的窗口框架边缘(至少在我所知道的Aero主题下)。
问题在于,WS_SIZEBOX与WS_THICKFRAME相同,而在Aero中,窗口无论是否可以调整大小都有thickframe。但是GetWindowRect函数认为不可调整大小的窗口更窄。
修复方法是:您可以使用WS_SIZEBOX创建窗口,调用GetWindowRect,然后使用SetWindowLongPtr(GWL_STYLE,...)关闭WS_SIZEBOX,但这将在客户端区域内创建一个丑陋的白色边框。
相反,保留WS_SIZEBOX启用,并在响应WM_GETMINMAXINFO消息时简单地返回ptMinTrackSize和ptMaxTraceSize的相同值到MINMAXINFO结构中,这将防止窗口可调整大小,并使GetWindowRect返回正确的数据。唯一的缺点是当鼠标指针经过窗口框架时,鼠标光标仍会更改为调整大小的光标,但这是迄今为止较小的问题。


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

0

GetWindowRect返回正确的值,但是需要显式指定窗口句柄。使用GetParent函数获取父窗口的句柄,而GetWindowRect将为您返回最大的RECT或者GetParent返回值为空。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接