窗体在 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 上显示错误的尺寸 - 如何获得真实尺寸?的主要内容,如果未能解决你的问题,请参考以下文章