Windows 7上GetWindowRect过小
我现在想解决的问题是,想自动找出窗口周围的边距大小。如果你有更好的方法,请直接分享,而不是回答这个问题。
为了解决这个问题,我决定截取一个测试窗口的屏幕,然后测量边距。这其实挺简单的,因为我觉得边距不可能是亮粉色的,不过我承认这有点像是偷懒的方法。我使用了GetWindowRect(py)来获取窗口的边界框,然后用PIL来截屏并裁剪到这个边界框。不过问题是,虽然裁剪操作正常,但这个边界框的尺寸并不准确。而Windows 7的“截图工具”能得到正确的尺寸。我该怎么做才能达到同样的效果呢?
6 个回答
在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也会返回正确的数据。唯一的缺点是,当鼠标指针经过窗口框架时,鼠标光标还是会变成调整大小的光标,但这算是小问题了。
我知道这个话题有点老了。不过我花了不少时间搜索,也经历了很多麻烦才让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()
我最初的想法如下,但如果你说的没错,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));
这样应该能给你正确的边界矩形。