GetWindowRect 在 Windows 7 上太小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3192232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:47:11  来源:igfitidea点击:

GetWindowRect too small on Windows 7

pythonwindowswinapipywin32

提问by Devin Jeanpierre

The actual problem I'm trying to solve is, I want to automatically find out the size of the margins around windows. If you can find a better way, please by all means answer that instead of this.

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

To do this I decided to take a screenshot of a test window and measure the margins. This is simple enough, as I expect no margins will ever be bright pink, but I admit it's a hack. I use GetWindowRect(py) to get the bounding box, and PILto grab a screenshot and crop to the bounding box. The problem is that while the crop operates correctly, the bounding box is not accurate. The Windows 7 "Snipping Tool" gets the correct dimensions. How may I do the same?

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

回答by paxdiablo

My first thoughts were listed below but if, as you state, you're certain that GetWindowRectis returning incorrect values, see RESOLUTIONfurther down.

我的第一个想法列在下面,但如果如您所说,您确定GetWindowRect返回的值不正确,请进一步查看RESOLUTION



"What's wrong with GetSystemMetrics(SM_CXBORDER)and GetSystemMetrics(SM_CYBORDER)?

GetSystemMetrics(SM_CXBORDER)GetSystemMetrics(SM_CYBORDER)怎么了?

The method you're using seems a very roundabout way of doing it and, if you can call GetWindowRect(), I'm pretty certain you can call GetSystemMetrics()as well.

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

One other possibility is to use GetWindowRectto get the entire bounding rectangle for the window and GetClientRectto get the bounding rectangle for the client (non-border) area.

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

This shouldgive you something like (100,200),(1000,900)and (112,227),(988,888)respectively and you can work out the top border as 227-200, bottom as 900-888, left as 112-100and right as 900-888(27,12,12,12).

应该分别给你类似(100,200),(1000,900)和 的东西(112,227),(988,888),你可以计算出顶部边框为227-200,底部为900-888,左为112-100和右为900-888(27,12,12,12)。



RESOLUTION:

解析度:

A bit of investigation turns up this. It's a thread from 2006 stating that you might not get the correct values from GetWindowsRect. The thread that pointed me to this stated:

有点调查结果这一点。这是 2006 年的一个帖子,指出您可能无法从GetWindowsRect. 向我指出这一点的线程指出:

Apps under Vista that are not linked with WINVER=6 will receive a misleading set of values here, that do not account for the extra padding of "glass" pixels Vista Aero applies to the window. This appears to happen even in Aero Basic (without Glass) to retain sizing consistency. The workaround (if you don't want to set WINVER=6) seems to be to dynamically bind to dwmapi.dll and use GetProcAddress() to obtain the DwmGetWindowAttribute() function, and call it with the DWMWA_EXTENDED_FRAME_BOUNDS argument to request the genuine window frame dimensions.

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

So basically, use something like (you may have to use ctypes to do this from Python):

所以基本上,使用类似的东西(你可能必须使用 ctypes 从 Python 中做到这一点):

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

and that should give you the correct bounding rectangle.

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

回答by Fenikso

I know it is a bit old topic. But it took quite a bit of searching and I went through the ctypes pain myself to get paxdiablo's solution working in Python. Just wanted to share working code sample for wxPython:

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

回答by Ray Gardener

GetWindowRect on Windows 7 appears to not include the right and bottom window frame edges (at least with the Aero theme afaik), ifthe window was created withoutthe WS_SIZEBOX style (i.e, you want a non-sizable window).

如果窗口是在没有WS_SIZEBOX 样式的情况下创建的(即,您想要一个不可调整大小的窗口),Windows 7 上的 GetWindowRect 似乎不包括右侧和底部窗口框架边缘(至少对于 Aero 主题 afaik )。

The problem is, WS_SIZEBOX is the same as WS_THICKFRAME, and on Aero, windows have the thickframe whether they can be resized or not. But the GetWindowRect function thinks that a non-resizable window is thinner.

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

The fix? You can create the window with WS_SIZEBOX, call GetWindowRect, then use SetWindowLongPtr(GWL_STYLE, ...) to turn off WS_SIZEBOX, but this will create an ugly white border inside the client area.

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

Instead, leave WS_SIZEBOX enabled, and simply return the same value for ptMinTrackSize and ptMaxTraceSize in the MINMAXINFO structure when responding to the WM_GETMINMAXINFO message. This will keep the window from being resizable and GetWindowRect will return proper data. The only downside is that the mouse cursor will still change to a resizing cursor when the mouse pointer goes over the window frame, but it's the lesser of the evils so far.

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

回答by JGShining

first, you call GetClientRect to retrive the client rectangle R1, then call AdjustWindowRectEx to calculate the accurate bounds according to R1.

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

回答by Survarium

GetWindowRect return correct values, but for explicit handle to the window. Use GetParent functionto get handle of the parent window, while GetWindoWRect return for you maximum RECT or GetParent return value is NULL.

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