C# Winforms调用GetWindowRect等API获取窗口位置尺寸的异常问题求解
解决方案
核心逻辑
通过组合不同API的能力静默处理所有场景,完全不需要提前恢复最小化窗口:
- 先通过
GetWindowPlacement判断窗口当前是否为最小化状态,无需触发窗口恢复即可拿到状态信息 - 非最小化窗口直接调用
DwmGetWindowAttribute获取EXTENDED_FRAME_BOUNDS属性值,得到排除阴影的准确窗口坐标 - 最小化窗口直接读取
GetWindowPlacement返回结构体中的rcNormalPosition字段,该字段存储的是窗口还原后的无阴影偏移的正常位置坐标
DPI适配处理
首先在项目的应用程序清单(manifest)中开启高DPI感知,避免系统自动缩放导致的坐标偏移。如果需要兼容不同DPI感知级别的目标窗口,调用GetDpiForWindow获取目标窗口的DPI值,按比例将逻辑坐标转换为物理屏幕坐标即可,转换公式为:物理坐标值 = 逻辑坐标值 * 目标窗口DPI / 96
完整实现示例
using System; using System.Runtime.InteropServices; // Rect结构体定义 [StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left; public int Top; public int Right; public int Bottom; public int Width => Right - Left; public int Height => Bottom - Top; } // WindowPlacement结构体定义 [StructLayout(LayoutKind.Sequential)] public struct WindowPlacement { public int length; public int flags; public int showCmd; public Point ptMinPosition; public Point ptMaxPosition; public Rect rcNormalPosition; } [StructLayout(LayoutKind.Sequential)] public struct Point { public int X; public int Y; } public static class WindowApi { private const int SW_SHOWMINIMIZED = 2; private const int DWMWA_EXTENDED_FRAME_BOUNDS = 9; [DllImport("user32.dll")] private static extern bool GetWindowPlacement(IntPtr hWnd, out WindowPlacement lpwndpl); [DllImport("dwmapi.dll")] private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute); [DllImport("user32.dll")] public static extern uint GetDpiForWindow(IntPtr hWnd); // 封装的获取窗口实际坐标方法 public static bool GetWindowActualRect(IntPtr hWnd, out Rect rect) { rect = default; WindowPlacement wp = default; wp.length = Marshal.SizeOf(wp); if (!GetWindowPlacement(hWnd, out wp)) return false; // 窗口是最小化状态,直接取正常位置矩形 if (wp.showCmd == SW_SHOWMINIMIZED) { rect = wp.rcNormalPosition; return true; } // 非最小化状态,调用DWM API取无阴影坐标 int dwmResult = DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(Rect))); return dwmResult == 0; } }
兼容注意事项
如果需要兼容Windows 7以下系统(DWM API不可用),可以在DwmGetWindowAttribute调用失败后,回退到GetWindowRect获取坐标后手动减去系统边框偏移量:Windows 7及以上系统默认窗口阴影偏移为左右各7px、底部7px,对应减去即可。
内容的提问来源于stack exchange,提问作者NullReferenceException
相关产品推荐
相关产品推荐

