GetWindowText失败主因是字符串封送错误:必须用StringBuilder预分配缓冲区,nMaxCount设为sb.Capacity,CharSet设为Auto或Unicode;FindWindowEx返回零需检查句柄层级、类名/标题匹配及SetLastError;GetWindowRect坐标需按DPI换算。

能直接调用,但必须用 DllImport 声明,且类型、调用约定、字符集三者错一个就崩——不是报错,而是静默失败或内存乱码。
为什么 MessageBox 调用成功了,但 GetWindowText 却拿不到字符串?
根本原因在字符串封送(marshaling)和缓冲区管理。Win32 的 GetWindowText 要求传入预分配的 StringBuilder,长度必须 ≥ 实际内容 + 1(结尾 null),且需显式指定 CharSet = CharSet.Auto 或明确匹配系统 API 版本(W 或 A 后缀)。
-
string参数默认按BSTR封送,只适用于输入;输出必须用StringBuilder - 忘记设
nMaxCount为sb.Capacity,会导致截断或越界读取 -
CharSet.Ansi在 Win10/11 上大概率返回空,因系统实际调用的是GetWindowTextW - 示例修正写法:
[DllImport("user32.dll", CharSet = CharSet.Auto)]<br>public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
FindWindowEx 总是返回 IntPtr.Zero?检查这三点
这个函数对句柄层级、窗口类名/标题的匹配极其敏感,且容易受 UAC 隔离、DPI 缩放、窗口线程归属影响。
- 父窗口句柄(
hwndParent)传IntPtr.Zero表示桌面级搜索,但若目标窗口属于其他会话(如服务进程),不可见 - 类名或窗口标题传
null表示通配,但传空字符串""会精确匹配空标题——这是常见误写 - 未加
SetLastError = true,导致无法用Marshal.GetLastWin32Error()查具体失败码(比如ERROR_ACCESS_DENIED) - 正确声明示例:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]<br>public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
GetWindowRect 返回的坐标在高 DPI 下不准?
Win32 默认返回物理像素坐标,而 .NET 的 Rectangle 是逻辑单位。DPI 缩放开启后(如 125%、150%),不转换就会偏移。
- 不要直接把
out Rectangle的值当屏幕坐标用,尤其做窗口拖拽或截图定位时 - 若需逻辑坐标,用
Graphics.FromHwnd(hWnd).DpiX / 96f换算缩放比;若需物理坐标,确保调用前已用SetProcessDpiAwarenessContext声明 DPI 感知 -
GetWindowRect本身不感知 DPI,它返回的就是屏幕真实像素——这点和GetClientRect不同 - 声明时务必用
CharSet.Auto或Unicode,否则结构体字段对齐可能出错
最常被忽略的是:所有涉及指针、句柄、结构体的 Win32 调用,都依赖你手动保证内存生命周期。比如传给 EnumWindows 的回调委托,若没用变量长期持有引用,GC 可能在回调执行前就回收它——崩溃无提示,只留 AccessViolationException。


















