views:

159

answers:

3

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 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?

+2  A: 

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.

paxdiablo
Well, it returns 1. 1 is not the width of any of the borders on that window. I'm pretty sure the values I want for that window are 8 and 30.
Devin Jeanpierre
How do you get 8 and 30? The correct looking frame seems to have the same width on the bottom as it does on the sides.
paxdiablo
I care about the top, too.
Devin Jeanpierre
So you're saying there are 30 pixels _above_ the client area and 8 on the bottom and sides?
paxdiablo
The second image illustrates the border of the window and the content (pink) perfectly. By my measurements, the edge between the end of the window and the end of the center content is 8 pixels on the left, right, and bottom, and 30 pixels at the top.
Devin Jeanpierre
I would actually print out the information you get back from GetWindowRect then use a tool like HyperSnap to work out if the co-ordinates are correct (we use that at work and it allows to to easily find the corners). It seems to me that either GetWindowsRect is lying (unlikely) or ImageGrab.grab(bbox) is deficient somehow. Personally, I'd be betting on the latter. Once you've established where the problem lies, we can think about alternatives (it's a pity the snipping tool doesn't have a cmdline interface to just grab the current window).
paxdiablo
One of the possible alternatives is to use GetWindowRect to get the whole thing then use GetClientRec to get the client area. You should be able to work out the four borders from that on a per Window basis.
paxdiablo
I already established which was lying, and it's GetWindowsRect. When I saw the outcome of the crop, I tested it myself manually. This is the origin of my statement, "the crop operates correctly".
Devin Jeanpierre
To clarify: the dimensions returned by GetWindowsRect are a window that is 306 wide and 328 tall (e.g. (175, 175, 481, 503)). This matches perfectly with the crop (and is even positioned correctly), but is smaller than the "Snipping Tool" crop (and cuts off the corners etc.)
Devin Jeanpierre
@Devin, see my final update. It may be that PIL is not linked with WINVER=6 and thus may be getting the incorrect values. It's worth testing the proposed solution to see if it gives you the correct values.
paxdiablo
Sorry it took so long to check up, I was procrastinating on actually testing because ctypes is a pain. It works perfectly!
Devin Jeanpierre
A: 

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

JGShining