如何在C#中正确定义和调用GetWindowDpiAwarenessContext?
在C#中正确调用GetWindowDpiAwarenessContext的方法
你踩了一个P/Invoke里的常见坑——GetWindowDpiAwarenessContext的返回值并不是普通整数,而是一个不透明的句柄类型,直接按int处理就会得到那些莫名其妙的数值。我来一步步帮你解决这个问题:
1. 正确的P/Invoke函数定义
首先必须在C#里正确声明这个Win32函数,返回值得用IntPtr(对应Win32中的DPI_AWARENESS_CONTEXT句柄),不能用int。另外还需要辅助函数GetAwarenessFromDpiAwarenessContext来解析这个句柄对应的具体感知级别:
using System; using System.Runtime.InteropServices; public static class Win32Api { /// <summary> /// 获取指定窗口的DPI感知上下文句柄 /// </summary> /// <param name="hWnd">窗口句柄,传入IntPtr.Zero表示当前进程主窗口</param> /// <returns>DPI感知上下文句柄,调用失败时返回IntPtr.Zero</returns> [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd); /// <summary> /// 从DPI感知上下文句柄中提取具体的感知级别 /// </summary> /// <param name="dpiContext">GetWindowDpiAwarenessContext返回的句柄</param> /// <returns>DPI感知级别数值,解析失败时返回-1</returns> [DllImport("user32.dll", SetLastError = true)] public static extern int GetAwarenessFromDpiAwarenessContext(IntPtr dpiContext); }
2. 正确理解DPI感知级别对应的数值
你之前预期的-1、-2等是错误认知,Win32中标准的DPI感知级别数值是:
0:DPI_AWARENESS_UNAWARE(不感知DPI)1:DPI_AWARENESS_SYSTEM_AWARE(系统级感知)2:DPI_AWARENESS_PER_MONITOR_AWARE(每显示器感知)3:DPI_AWARENESS_PER_MONITOR_AWARE_V2(每显示器感知V2,仅Windows 10 1703+支持)
只有当GetAwarenessFromDpiAwarenessContext返回-1时,才说明解析失败,这时可以通过Marshal.GetLastWin32Error()获取具体错误码。
3. 完整的调用流程示例
下面是包含错误处理的完整调用代码:
// 替换成你要检测的目标窗口句柄 IntPtr targetWindowHandle = /* 你的窗口句柄 */; // 获取DPI感知上下文句柄 IntPtr dpiContext = Win32Api.GetWindowDpiAwarenessContext(targetWindowHandle); if (dpiContext == IntPtr.Zero) { int errorCode = Marshal.GetLastWin32Error(); Console.WriteLine($"调用GetWindowDpiAwarenessContext失败,错误码:{errorCode}"); return; } // 解析具体的DPI感知级别 int awarenessLevel = Win32Api.GetAwarenessFromDpiAwarenessContext(dpiContext); switch (awarenessLevel) { case 0: Console.WriteLine("窗口DPI感知级别:不感知(UNAWARE)"); break; case 1: Console.WriteLine("窗口DPI感知级别:系统感知(SYSTEM_AWARE)"); break; case 2: Console.WriteLine("窗口DPI感知级别:每显示器感知(PER_MONITOR_AWARE)"); break; case 3: Console.WriteLine("窗口DPI感知级别:每显示器感知V2(PER_MONITOR_AWARE_V2)"); break; case -1: int parseError = Marshal.GetLastWin32Error(); Console.WriteLine($"解析DPI感知上下文失败,错误码:{parseError}"); break; default: Console.WriteLine($"未知的DPI感知级别:{awarenessLevel}"); break; }
4. 解决你遇到的两个问题
- 返回奇怪数值而非预期值:因为你之前错误地将返回值声明为
int,而DPI_AWARENESS_CONTEXT是指针类型,那些数字其实是指针的内存地址,不是错误码或感知级别。改用IntPtr后就能正确获取句柄,再通过辅助函数解析出真实级别。 - 同一窗口后续调用返回值变化:如果用正确定义后仍出现这种情况,可能是目标窗口的DPI感知状态被其所属进程修改了;也可能是你之前错误调用导致获取到无效的随机指针值,现在用正确方式应该能得到稳定结果。
额外注意事项
- 该函数仅支持Windows 10 1607(周年更新)及以上版本,在旧系统调用会返回
IntPtr.Zero,错误码为120(ERROR_CALL_NOT_IMPLEMENTED)。 - 访问其他进程的窗口时,要确保你的程序有足够权限,否则可能调用失败。
内容的提问来源于stack exchange,提问作者Kenneth Evans




