开发者

GetWindowRect too small on Windows 7

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 PIL to grab a screenshot and cro开发者_运维百科p 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?


My first thoughts were listed below but if, as you state, you're certain that GetWindowRect is returning incorrect values, see RESOLUTION further down.


"What's wrong with GetSystemMetrics(SM_CXBORDER) and 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.

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

This should give 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-100 and right as 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:

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.

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

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

and that should give you the correct bounding rectangle.


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:

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 on Windows 7 appears to not include the right and bottom window frame edges (at least with the Aero theme afaik), if the window was created without the WS_SIZEBOX style (i.e, you want a non-sizable window).

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.

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.

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.


DwmGetWindowAttribute

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


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


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜