Form tells wrong size on Windows 8 — how to get re

2019-01-18 15:11发布

Having a WinForms form with form border style set to Sizable on Windows 8, the DesktopBounds property tells the correct values:

enter image description here

In contrast, when having a form border style of FixedDialog, the values are wrong:

enter image description here

On Windows XP, the values are always correct:

enter image description here

enter image description here

My question is:

How to get the real size of a Window including the complete non-client area?

Update 1:

Seems that it is related to this SO question. I'll try and see whether this would solve my issue here, too.

Update 2:

Just for completeness, here are the results from a VMware Windows 7:

enter image description here

enter image description here

Update 3:

Finally found a solution which involves using the DwmGetWindowAttribute function together with the DWMWA_EXTENDED_FRAME_BOUNDS value. I'll post an answer below.

2条回答
家丑人穷心不美
2楼-- · 2019-01-18 15:49

I don't think "wrong" is quite the right way to put it.. you are seeing values you don't understand, but that's not always the same as wrong. The real question is what is the actual problem you are trying to solve by getting the window bounds?

Have you tried the Win32 GetWindowRect method? I wonder what that shows.

One hack you could try would be to detect the OS and account for these.

To determine the OS in C#: http://support.microsoft.com/kb/304283 (that sample doesn't mention Windows 8 specifically, but I assume the SDK has been updated for it)

查看更多
劫难
3楼-- · 2019-01-18 15:53

To answer my own question, I finally found a solution which involves using the DwmGetWindowAttribute function together with the DWMWA_EXTENDED_FRAME_BOUNDS value

The answer was inspired by this source code which presents a function that seems to work on all system. The core is a function:

public static Rectangle GetWindowRectangle(IntPtr handle)
{
    if (Environment.OSVersion.Version.Major < 6)
    {
        return GetWindowRect(handle);
    }
    else
    {
        Rectangle rectangle;
        return DWMWA_EXTENDED_FRAME_BOUNDS(handle, out rectangle) 
                   ? rectangle 
                   : GetWindowRect(handle);
    }
}

Full code is provided below:

public static class WindowHelper
{
    // https://code.google.com/p/zscreen/source/browse/trunk/ZScreenLib/Global/GraphicsCore.cs?r=1349

    /// <summary>
    /// Get real window size, no matter whether Win XP, Win Vista, 7 or 8.
    /// </summary>
    public static Rectangle GetWindowRectangle(IntPtr handle)
    {
        if (Environment.OSVersion.Version.Major < 6)
        {
            return GetWindowRect(handle);
        }
        else
        {
            Rectangle rectangle;
            return DWMWA_EXTENDED_FRAME_BOUNDS(handle, out rectangle) ? rectangle : GetWindowRect(handle);
        }
    }

    [DllImport(@"dwmapi.dll")]
    private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);

    private enum Dwmwindowattribute
    {
        DwmwaExtendedFrameBounds = 9
    }

    [Serializable, StructLayout(LayoutKind.Sequential)]
    private struct Rect
    {
        // ReSharper disable MemberCanBePrivate.Local
        // ReSharper disable FieldCanBeMadeReadOnly.Local
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        // ReSharper restore FieldCanBeMadeReadOnly.Local
        // ReSharper restore MemberCanBePrivate.Local

        public Rectangle ToRectangle()
        {
            return Rectangle.FromLTRB(Left, Top, Right, Bottom);
        }
    }

    private static bool DWMWA_EXTENDED_FRAME_BOUNDS(IntPtr handle, out Rectangle rectangle)
    {
        Rect rect;
        var result = DwmGetWindowAttribute(handle, (int)Dwmwindowattribute.DwmwaExtendedFrameBounds,
            out rect, Marshal.SizeOf(typeof(Rect)));
        rectangle = rect.ToRectangle();
        return result >= 0;
    }

    [DllImport(@"user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect);

    private static Rectangle GetWindowRect(IntPtr handle)
    {
        Rect rect;
        GetWindowRect(handle, out rect);
        return rect.ToRectangle();
    }
}
查看更多
登录 后发表回答