这是一个在 Windows 平台下进行 GUI 编程时非常核心和常用的 API 函数。

(图片来源网络,侵删)
什么是 EnumWindows?
EnumWindows 是 Windows API (应用程序编程接口) 中的一个函数,它属于 user32.dll 动态链接库,它的主要作用是枚举(遍历)桌面顶层所有的顶级窗口。
“顶级窗口”指的是那些没有父窗口,或者其父窗口是桌面窗口的窗口,你打开的记事本、浏览器、文件夹窗口等,都是顶级窗口,一个对话框(如“另存为”对话框)通常不是一个顶级窗口,它的父窗口是打开它的那个应用程序窗口。
函数原型
你可以在 winuser.h 头文件中找到它的声明,其原型如下:
BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam );
参数详解:
-
lpEnumFunc(Window Enumeration Procedure)
(图片来源网络,侵删)- 这是一个函数指针,指向一个由你自己定义的回调函数。
EnumWindows每找到一个顶级窗口,就会调用一次这个回调函数,并将找到的窗口的句柄作为参数传递给它。- 回调函数的原型必须是固定的:
BOOL CALLBACK EnumWindowsProc( HWND hwnd, // 找到的窗口的句柄 LPARAM lParam // 你在 EnumWindows 中传入的 lParam );
HWND hwnd: 一个窗口句柄,代表当前被枚举到的窗口。LPARAM lParam: 一个 32 位值,这是你在调用EnumWindows时传入的第二个参数,你可以用它来向回调函数传递自定义数据。- 返回值:
- 如果回调函数返回
TRUE,EnumWindows会继续枚举下一个窗口。 - 如果回调函数返回
FALSE,EnumWindows会立即停止枚举过程。
- 如果回调函数返回
-
lParam- 一个 32 位的参数,你可以向它传递任何你想要的数据(一个指向结构体的指针、一个整型值等)。
- 这个值会原封不动地传递给你的回调函数
EnumWindowsProc的第二个参数。 - 如果你不需要向回调函数传递数据,可以简单地将其设置为
0。
返回值:
TRUE: 枚举成功完成(或者被回调函数主动终止)。FALSE: 枚举失败,你可以调用GetLastError()函数来获取具体的错误代码。
如何使用 EnumWindows (一个完整的示例)
下面是一个完整的 C 语言示例,它演示了如何使用 EnumWindows 来列出桌面所有顶级窗口的标题和类名。
步骤:
- 包含必要的头文件 (
windows.h)。 - 定义回调函数
EnumWindowsProc。 - 在
main函数中调用EnumWindows,并将回调函数的地址作为参数传入。 - 在回调函数中,使用
GetWindowText和GetClassName等函数来获取窗口信息并打印。
示例代码:
#include <windows.h>
#include <stdio.h>
// 1. 定义回调函数
// 这个函数会被 EnumWindows 每次找到一个窗口时调用
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
// lParam 是我们在 EnumWindows 中传入的,这里我们用它来计数
int* count = (int*)lParam;
// 定义用于存储窗口标题和类名的缓冲区
char windowTitle[256];
char windowClass[256];
// 获取窗口标题
// GetWindowText 只获取标题可见的窗口
if (GetWindowText(hwnd, windowTitle, sizeof(windowTitle)) > 0) {
// 获取窗口类名
GetClassName(hwnd, windowClass, sizeof(windowClass));
// 打印窗口信息
// hwnd 是窗口的唯一标识符,可以理解为窗口的“身份证号”
printf("句柄: 0x%p, 标题: %s, 类名: %s\n", hwnd, windowTitle, windowClass);
}
// 增加计数器
(*count)++;
// 返回 TRUE 以继续枚举下一个窗口
// 如果想停止枚举,可以在这里返回 FALSE
return TRUE;
}
int main() {
// 用于统计找到的窗口数量
int windowCount = 0;
printf("开始枚举顶层窗口...\n");
// 2. 调用 EnumWindows,传入回调函数和我们的计数器
// 将 windowCount 的地址作为 lParam 传递,这样回调函数就能修改它
BOOL result = EnumWindows(EnumWindowsProc, (LPARAM)&windowCount);
if (result) {
printf("\n枚举完成,共找到 %d 个顶级窗口,\n", windowCount);
} else {
printf("枚举失败,错误码: %lu\n", GetLastError());
}
return 0;
}
如何编译和运行:
- 将上述代码保存为
enum_windows.c。 - 使用 Visual Studio 的开发者命令提示工具进行编译:
cl enum_windows.c
- 运行生成的
enum_windows.exe,你将看到一个命令行窗口,列出了当前桌面上所有可见窗口的详细信息。
高级用法:查找特定窗口
EnumWindows 最常见的用途之一是查找一个满足特定条件的窗口(标题包含“记事本”的窗口)。
我们可以通过修改回调函数来实现这一点。

(图片来源网络,侵删)
示例:查找所有标题包含“记事本”的窗口
#include <windows.h>
#include <stdio.h>
#include <tchar.h> // 使用 _t 系列函数以支持 Unicode
// 回调函数
BOOL CALLBACK FindNotepadProc(HWND hwnd, LPARAM lParam) {
TCHAR windowTitle[256];
// 获取窗口标题
if (GetWindowText(hwnd, windowTitle, sizeof(windowTitle) / sizeof(TCHAR)) > 0) {
// 检查标题是否包含“记事本”
// _tcsstr 是一个通用的字符串查找函数,在 ANSI 下是 strstr,在 Unicode 下是 wcsstr
if (_tcsstr(windowTitle, _T("记事本")) != NULL) {
// 如果找到了,打印信息并返回 FALSE 来停止枚举
// 因为我们只需要一个,或者找到所有,这里我们打印所有找到的
printf("找到目标窗口! 句柄: 0x%p, 标题: %s\n", hwnd, windowTitle);
// 如果只想找一个,可以在这里返回 FALSE
// return FALSE;
}
}
// 继续枚举
return TRUE;
}
int main() {
printf("正在查找标题包含'记事本'的窗口...\n");
EnumWindows(FindNotepadProc, 0);
printf("查找结束,\n");
return 0;
}
注意事项
- 线程安全:回调函数
EnumWindowsProc是在EnumWindows内部创建的辅助线程上调用的,要避免在回调函数中与主线程共享未受保护的资源,如果需要共享数据,请使用同步机制(如互斥锁 Mutex)。 - 性能:如果桌面上有大量的窗口,
EnumWindows可能会花费一些时间,在回调函数中应避免执行耗时操作。 - Unicode 支持:现代 Windows 应用广泛使用 Unicode,为了编写兼容 ANSI 和 Unicode 的代码,推荐使用带
_t前缀的通用 API 函数,_tcscpy、_tcslen、GetWindowTextLength等。GetWindowText会根据编译环境自动处理字符集。 EnumChildWindows:如果你需要枚举一个特定窗口的子窗口,可以使用EnumChildWindows函数,它的用法和EnumWindows非常相似,只是它需要一个父窗口的句柄作为第一个参数。
EnumWindows 是 Windows 系统编程中一个功能强大且基础的工具,通过它,你可以:
- 获取系统上所有窗口的列表。
- 查找特定属性(如标题、类名)的窗口。
- 对每个窗口执行操作(发送消息、修改样式、获取窗口文本等)。
掌握 EnumWindows 和回调函数的用法,是进行 Windows 桌面自动化、插件开发、系统监控等任务的关键一步。
