7

Sizable在Windows 8 上,将表单边框样式设置为 WinForms 表单,该DesktopBounds属性会显示正确的值:

在此处输入图像描述

相反,当表单边框样式为 时FixedDialog,值是错误的:

在此处输入图像描述

在 Windows XP 上,这些值总是正确的:

在此处输入图像描述

在此处输入图像描述

我的问题是:

如何获得包括完整非客户区在内的窗口的实际大小?

更新1:

似乎它与这个 SO question 有关。我会尝试看看这是否也能解决我的问题。

更新 2:

为了完整起见,以下是 VMware Windows 7 的结果:

在此处输入图像描述

在此处输入图像描述

更新 3:

终于找到了一个解决方案,该解决方案涉及将DwmGetWindowAttributefunctionDWMWA_EXTENDED_FRAME_BOUNDSvalue一起使用。我将在下面发布答案。

4

2 回答 2

9

为了回答我自己的问题,我终于找到了一个解决方案,该解决方案涉及将DwmGetWindowAttribute函数DWMWA_EXTENDED_FRAME_BOUNDS值一起使用

答案的灵感来自这个源代码,它提供了一个似乎适用于所有系统的功能。核心是一个函数:

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);
    }
}

完整代码如下:

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();
    }
}
于 2013-05-10T16:13:34.217 回答
-3

我不认为“错误”是正确的表达方式。你看到的是你不理解的价值观,但这并不总是与错误相同。真正的问题是您试图通过获取窗口边界来解决的实际问题是什么?

你试过Win32的GetWindowRect方法吗?我想知道这表明了什么。

您可以尝试的一种技巧是检测操作系统并解决这些问题。

要确定 C# 中的操作系统:http: //support.microsoft.com/kb/304283(该示例没有特别提到 Windows 8,但我认为 SDK 已经更新)

于 2013-05-10T15:11:39.680 回答