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.
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?
Answers
To work around the issues with GetWindowRect
returning values that include invisible borders in Windows 10, you can use the DwmGetWindowAttribute
function to get the actual window coordinates, and then use these coordinates to position your windows correctly. While it might seem cumbersome to change all instances where you get window coordinates, it is necessary due to changes in how Windows 10 handles window borders and DPI scaling.
Here’s a detailed approach to address your problem:
Step-by-Step Solution
-
Use
DwmGetWindowAttribute
to Get the Actual Window Coordinates: UseDwmGetWindowAttribute
with theDWMWA_EXTENDED_FRAME_BOUNDS
attribute to get the real coordinates of the window. This function retrieves the window's size and position as it is displayed on the screen, which includes any shadows or borders.#include <dwmapi.h> #pragma comment(lib, "dwmapi.lib") RECT GetActualWindowRect(HWND hwnd) { RECT rect; HRESULT hr = DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(rect)); if (SUCCEEDED(hr)) { return rect; } else { // Fallback to GetWindowRect if DwmGetWindowAttribute fails GetWindowRect(hwnd, &rect); return rect; } }
-
Set Window Position Correctly: When you need to set the window position, you must account for the difference between the
GetWindowRect
result and the actual frame bounds. This involves reversing the process by calculating the difference between the extended frame bounds and the window rect.void SetActualWindowPosition(HWND hwnd, int x, int y, int width, int height) { RECT windowRect, frameRect; GetWindowRect(hwnd, &windowRect); DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect)); int borderX = (frameRect.right - frameRect.left) - (windowRect.right - windowRect.left); int borderY = (frameRect.bottom - frameRect.top) - (windowRect.bottom - windowRect.top); SetWindowPos(hwnd, NULL, x, y, width - borderX, height - borderY, SWP_NOZORDER | SWP_NOACTIVATE); }
-
Enable DPI Awareness: Ensure that your application is DPI-aware, which can affect how window coordinates are handled. This involves setting the appropriate DPI awareness context for your application. If you're using a manifest file, include the DPI awareness section:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> </windowsSettings> </application>
Alternatively, call
SetProcessDpiAwareness
orSetProcessDpiAwarenessContext
in your code:#include <shellscalingapi.h> #pragma comment(lib, "shcore.lib") void EnableDPIAwareness() { SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); }
Make sure to call
EnableDPIAwareness
early in your application's initialization code. -
Compatibility Manifest: Ensure your application’s manifest specifies compatibility settings for Windows 10. This ensures that Windows handles certain legacy behaviors correctly.
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <windowsCompatibility> <maxTestedVersion version="10.0.19041.0"/> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Windows Vista --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- Windows 7 --> <supportedOS Id="{4f34fdf5-96e0-4f5e-97f8-16e08f5fdf3e}"/> <!-- Windows 8 --> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 8.1 --> <supportedOS Id="{a8b865dd-2e3d-4bfe-8e13-9c219dff4ad2}"/> <!-- Windows 10 --> </windowsCompatibility> </application> </compatibility>
Summary
By using DwmGetWindowAttribute
to get the true window coordinates and adjusting for the invisible borders, you can position windows accurately on Windows 10. Ensuring your application is DPI-aware and using the appropriate compatibility settings in the manifest will further help in handling different versions of Windows correctly. While this approach involves modifying how you get and set window positions, it provides a reliable way to manage window coordinates in modern Windows environments.