1

I want to move and resize an application's window (ex: Notepad). I tried both MoveWindow and SetWindowPos, and both has the exact same issue. The new position/size is always inaccurate.

MoveWindow(handle, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, true);
SetWindowPos(handle, IntPtr.Zero, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, 0);

I tested this on different PCs (both running Windows 10) and I noticed a pattern. On the first PC, the "drift" was X:+7, Y:0, W: -14, H: -7. On the second PC, the "drift" was X:+8, Y:0, W:-16, H-8.

I did test it on a third PC, but the result was way worse which I'm guessing it has something to do with it having 2 monitors.

I can see the "drift" with my own eyes, it's especially clear when I feed it X:0 and Y:0. I don't understand why this is happening though or how I can fix it.

This is the function I use to get a Window's (ex: Notepad) position and dimensions:

public static Rectangle GetWindowRectangle(IntPtr handle)
{
    Rect rect = new Rect();
    if (Environment.OSVersion.Version.Major >= 6)
    {
        int size = Marshal.SizeOf(typeof(Rect));
        DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);
    }
    else if (Environment.OSVersion.Version.Major < 6 || rect.ToRectangle().Width == 0)
    {
        GetWindowRect(handle, out rect);
    }

    return rect.ToRectangle();
}
root
  • 143
  • 17

2 Answers2

5

You've ignored the shadow of the window. In win10, you will use DwmGetWindowAttribute + DWMWA_EXTENDED_FRAME_BOUNDS in GetWindowRectangle, and according to the documentation, this will get the window bounds excluding the drop shadow.

And in Windows Vista and later, the Window Rect now includes the area occupied by the drop shadow. So, function MoveWindow and SetWindowPos think that the position/size you pass includes the shadow.

if you want to move and resize the area that excludes the shadow, then you need to first calculate the size of the shadow (C++ sample):

RECT exclude_shadow = {}, include_shadow = {};
GetWindowRectangle(hwnd,&exclude_shadow);
GetWindowRect(hwnd, &include_shadow);
RECT shadow = {};
shadow.left = include_shadow.left - exclude_shadow.left; // -7
shadow.right = include_shadow.right - exclude_shadow.right; // +7
shadow.top = include_shadow.top - exclude_shadow.top; //0
shadow.bottom = include_shadow.bottom - exclude_shadow.bottom; // +7

LONG Width = include_shadow.right - include_shadow.left;
LONG Height = include_shadow.bottom - include_shadow.top;

//SetWindowPos(hwnd, NULL, 0 + shadow.left, 0 + shadow.top, Width, Height, 0);
SetWindowPos(hwnd, NULL, 0 + shadow.left, 0 + shadow.top, 0, 0, SWP_NOSIZE); // use SWP_NOSIZE if you want to keep the size.
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • 1
    It took a lot of tinkering to figure it out but I finally managed to get it to work correctly based on the sample you provided, thank you. I chose yours as the answer but I will post another "answer" including the C# code I ended up using in case it helps someone in the future. – root Jan 07 '21 at 07:40
4

All credit goes to Drake Wu - MSFT

This is the working C# code I ended up using in case it helps someone in the future.
PS: window is a Rect containing the new position and dimensions of the window.

 public static void SetWindow(Rect window, IntPtr handle)
    {
        Rect excludeShadow = new Rect();
        Rect includeShadow = new Rect();

        GetWindowRectangle(handle, out excludeShadow);
        GetWindowRect(handle, out includeShadow);

        Rect shadow = new Rect();

        shadow.Left = includeShadow.Left - excludeShadow.Left;
        shadow.Right = includeShadow.Right - excludeShadow.Right;
        shadow.Top = includeShadow.Top - excludeShadow.Top;
        shadow.Bottom = includeShadow.Bottom - excludeShadow.Bottom;

        int width = (window.Right + shadow.Right) - (window.Left + shadow.Left);
        int height = (window.Bottom + shadow.Bottom) - (window.Top - shadow.Top);

        SetWindowPos(handle, IntPtr.Zero, window.Left + shadow.Left, window.Top + shadow.Top, width, height, 0);
    }
root
  • 143
  • 17