EnumWindows 是遍历所有顶级窗口最直接可靠的方式,仅枚举 hWndParent == nullptr 的窗口,需用 IsWindow 过滤无效句柄,并通过 GetWindowTextLength + std::vector 分配缓冲区安全获取标题。

用 EnumWindows 获取所有顶级窗口句柄
Windows API 提供了 EnumWindows 函数,它是遍历所有顶级窗口最直接、最可靠的方式。它不依赖进程枚举或 UI Automation,也不需要提升权限,只要调用线程有基本的桌面访问权限(通常都有)就能工作。
关键点在于:它只枚举「可见或不可见」的顶级窗口(即 hWndParent == nullptr 的窗口),但不包括子窗口、消息窗(如 HWND_MESSAGE)、或已销毁但未清理的句柄残留。
-
EnumWindows第二个参数是用户传入的LPARAM,可用于传递自定义数据(比如std::vector<hwnd>*</hwnd>) - 回调函数必须声明为
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam),且需用__stdcall调用约定(MSVC 下CALLBACK宏已定义) - 返回
TRUE继续枚举,FALSE中断
如何过滤掉无效或系统内部窗口
直接拿到的 HWND 不一定都能安全使用——有些窗口已销毁但句柄未回收,有些是系统内部窗口(如 WorkerW、Shell_TrayWnd 的子级),有些没有标题或类名。常见错误是调用 GetWindowText 或 GetClassName 后崩溃或返回空字符串。
- 先用
IsWindow(hwnd)排除已销毁句柄 - 用
GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE判断是否可见(可选,按需保留隐藏窗口) - 用
GetWindowTextLength(hwnd) > 0粗筛有标题的窗口(注意:很多系统窗口标题为空,但仍有用) - 避免对
Progman、WorkerW、SHELLDLL_DefView等类名做业务逻辑处理——它们属于桌面管理内部结构,内容不稳定
获取窗口标题和类名的实际写法
不能直接用 std::string 接收宽字符结果。Windows 窗口文本默认是 UTF-16,GetWindowText 和 GetClassName 都操作 LPWSTR。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 分配足够缓冲区:用
GetWindowTextLength(hwnd) + 1算长度,再std::vector<wchar_t></wchar_t>分配 - 调用后检查返回值是否 > 0,否则跳过(比如窗口无标题)
- 转换为 UTF-8 可选,但不是必须;若要输出到控制台,确保终端支持 UTF-16 或做了正确转码
- 不要用
std::wstring::c_str()直接传给GetWindowText—— 容量可能不足,且c_str()返回 const 指针
示例片段:
std::vector<wchar_t> buf(256);
int len = GetWindowTextLength(hwnd);
if (len > 0 && len < 255) {
GetWindowText(hwnd, buf.data(), static_cast<int>(buf.size()));
std::wstring title(buf.data());
}
为什么不用 FindWindowEx 或遍历进程?
FindWindowEx 是找特定窗口,不是遍历;而通过 EnumProcesses → EnumThreadWindows 路径太重、权限要求高、且无法覆盖无主窗口(如某些 COM 窗口、UIA 注册窗口),还容易漏掉后台线程创建的顶级窗口。
-
EnumWindows是系统维护的全局窗口链表快照,原子性强、开销低 - 某些窗口(如任务栏通知区域弹窗)不归属任何用户进程,仅靠进程遍历会丢失
- UWP 应用的顶层窗口也能被
EnumWindows捕获(尽管其GetClassName可能返回Windows.UI.Core.CoreWindow) - 唯一限制:不能跨桌面(如登录屏幕、锁屏桌面),
EnumWindows只作用于当前交互式桌面
真正容易被忽略的是:你拿到的 HWND 可能在回调返回后立刻失效(比如窗口刚好被关闭),后续操作前务必再次调用 IsWindow 校验。

















