GetWindowRect returns a size including "invisible" borders

Deanna picture Deanna · Dec 7, 2015 · Viewed 9.8k times · Source

I'm working on an app that positions windows on the screen in a grid style. When Running this on Windows 10, there is a huge gap between the windows. Further investigation shows that GetWindowRect is returning unexpected values, including an invisible border, but I can't get it to return the real values with the visible border.

1) This thread suggests this is by design and you can "fix" it by linking with winver=6. My environment does not allow this but I've tried changing the PE MajorOperatingSystemVersion and MajorSubsystemVersion to 6 with no affect

2) That same thread also suggests using DwmGetWindowAttribute with DWMWA_EXTENDED_FRAME_BOUNDS to get the real coordinates from DWM, which works, but means changing everywhere that gets the window coordinates. It also doesn't allow the value to be set, leaving us to reverse the process to be able to set the window size.

3) This question suggests it's lack of the DPI awareness in the process. Neither setting the DPI awareness flag in the manifest, or calling SetProcessDpiAwareness had any result.

4) On a whim, I've also tried adding the Windows Vista, 7, 8, 8.1 and 10 compatibility flags, and the Windows themes manifest with no change.

Screenshot of a "fullscreen" window with gaps all round This window is moved to 0x0, 1280x1024, supposedly to fill the entire screen, and when querying the coordinates back, we get the same values. The window however is actually 14 pixels narrower, to take into account the border on older versions of Windows.

How can I convince Windows to let me work with the real window coordinates?

Answer

Barmak Shemirani picture Barmak Shemirani · Dec 7, 2015

Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing. The borders might look like this: 7,0,7,7 (left, top, right, bottom)

When you call SetWindowPos to put the window at this coordinates:
0, 0, 1280, 1024

The window will pick those exact coordinates, and GetWindowRect will return the same coordinates. But visually, the window appears to be here:
7, 0, 1273, 1017

You can fool the window and tell it to go here instead:
-7, 0, 1287, 1031

To do that, we get Windows 10 border thickness:

RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));

//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`

RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;

//border should be `7, 0, 7, 7`

Then offset the rectangle like so:

rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;

//new rect should be `-7, 0, 1287, 1031`

Unless there is a simpler solution!