窗体在 Windows 8 上显示错误的尺寸 - 如何获得真实尺寸?

Posted

技术标签:

【中文标题】窗体在 Windows 8 上显示错误的尺寸 - 如何获得真实尺寸?【英文标题】:Form tells wrong size on Windows 8 — how to get real size? 【发布时间】:2013-05-05 07:02:47 【问题描述】:

在 Windows 8 上将表单边框样式设置为 Sizable 的 WinForms 表单,DesktopBounds property 告诉正确的值:

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

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

我的问题是:

如何获取包含完整非客户区的窗口的实际大小?

更新 1:

好像和this SO question有关。我会尝试看看这是否也能解决我的问题。

更新 2:

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

更新 3:

终于找到了一个解决方案,将DwmGetWindowAttribute function 与DWMWA_EXTENDED_FRAME_BOUNDS value 一起使用。我将在下面发布答案。

【问题讨论】:

如果您仔细观察,Windows XP 会表现出相同的行为。只是 Windows 边框是单个像素宽,而不是 Windows 8 中的五个像素(在 XP 中 Fixed 和 Sizable 之间的差异是 2,而在 Windows 8 中是 10)。 是什么让您假设尺寸错误?如果您在更改 FormBorderStyle 时查看 Visual Studio 设计器,您实际上会看到它缩小了 @JustinNiessner 是的,我确实看到了; Windows XP 版本返回的尺寸实际上仍然反映了实际的表单大小,而在 Windows 8 上,它们不会。 在 Windows 10 上,如果您在窗口首次被绘制到屏幕之前调用 DwmGetWindowAttribute()DWMWA_EXTENDED_FRAME_BOUNDS(例如,为了在屏幕上正确放置一个新窗口),那么您将获得与GetWindowRect() 相同的结果 - 即包括不可见的边框。您必须等到窗口被渲染后才能给出正确的结果。 【参考方案1】:

为了回答我自己的问题,我终于找到了一个解决方案,它涉及将DwmGetWindowAttribute function 与DWMWA_EXTENDED_FRAME_BOUNDS value 一起使用

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

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

【讨论】:

我正在使用与此非常相似的代码,并发现在许多高 DPI 显示的情况下,API 函数只返回 0 并回退到 GetWindowRect() 调用。 该来源似乎已移至此处:github.com/ShareX/ShareX/blob/…【参考方案2】:

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

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

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

要确定 C# 中的操作系统:http://support.microsoft.com/kb/304283(该示例没有具体提及 Windows 8,但我认为 SDK 已针对它进行了更新)

【讨论】:

谢谢。您将如何进行操作系统切换?我无法想象一种计算真实值的方法。即使在检查了SystemInformation 属性之后。 刚刚试过GetWindowRect,它返回完全相同的值。 编辑了有关如何确定操作系统的信息。

以上是关于窗体在 Windows 8 上显示错误的尺寸 - 如何获得真实尺寸?的主要内容,如果未能解决你的问题,请参考以下文章

delphi7中怎样调整程序适应不同的显示器尺寸?

C# Windows 窗体组合框显示错误的“显示值”

C# winfrom 窗体的StartPosition 属性

你如何在 Windows 窗体上显示动画 GIF (c#)

拖放时将桌面图标移动到Windows窗体上?

添加新记录时,Visual Studio SQL 默认值未显示在 Windows 窗体上