Windows 7上GetWindowRect过小

14 投票
6 回答
7853 浏览
提问于 2025-04-16 00:55

我现在想解决的问题是,想自动找出窗口周围的边距大小。如果你有更好的方法,请直接分享,而不是回答这个问题。

为了解决这个问题,我决定截取一个测试窗口的屏幕,然后测量边距。这其实挺简单的,因为我觉得边距不可能是亮粉色的,不过我承认这有点像是偷懒的方法。我使用了GetWindowRectpy)来获取窗口的边界框,然后用PIL来截屏并裁剪到这个边界框。不过问题是,虽然裁剪操作正常,但这个边界框的尺寸并不准确。而Windows 7的“截图工具”能得到正确的尺寸。我该怎么做才能达到同样的效果呢?

6 个回答

2

在Windows 7上,使用GetWindowRect这个函数时,似乎不会把窗口的右边和底边的框架边缘算进去(至少在Aero主题下是这样),前提是这个窗口是没有使用WS_SIZEBOX样式创建的,也就是说,你想要一个不能调整大小的窗口。

问题在于,WS_SIZEBOX和WS_THICKFRAME是一样的,而在Aero主题下,不管窗口能不能调整大小,都会有一个厚框架。但GetWindowRect这个函数却认为不能调整大小的窗口是比较窄的。

解决办法是什么呢?你可以在创建窗口时使用WS_SIZEBOX,然后调用GetWindowRect,接着用SetWindowLongPtr(GWL_STYLE, ...)把WS_SIZEBOX关掉,但这样会在客户区里面产生一个难看的白色边框。

更好的方法是,保持WS_SIZEBOX开启,当你响应WM_GETMINMAXINFO消息时,在MINMAXINFO结构中返回ptMinTrackSize和ptMaxTraceSize的相同值。这样可以防止窗口被调整大小,同时GetWindowRect也会返回正确的数据。唯一的缺点是,当鼠标指针经过窗口框架时,鼠标光标还是会变成调整大小的光标,但这算是小问题了。

4

我知道这个话题有点老了。不过我花了不少时间搜索,也经历了很多麻烦才让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()
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)。


解决方案:

经过一些调查,我发现了 这个帖子。这是一个2006年的讨论,提到你可能从 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));

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

撰写回答