EnumWindows如何枚举所有窗口句柄?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

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

c语言EnumWindows
(图片来源网络,侵删)

什么是 EnumWindows

EnumWindows 是 Windows API (应用程序编程接口) 中的一个函数,它属于 user32.dll 动态链接库,它的主要作用是枚举(遍历)桌面顶层所有的顶级窗口

“顶级窗口”指的是那些没有父窗口,或者其父窗口是桌面窗口的窗口,你打开的记事本、浏览器、文件夹窗口等,都是顶级窗口,一个对话框(如“另存为”对话框)通常不是一个顶级窗口,它的父窗口是打开它的那个应用程序窗口。

函数原型

你可以在 winuser.h 头文件中找到它的声明,其原型如下:

BOOL EnumWindows(
  WNDENUMPROC lpEnumFunc,
  LPARAM      lParam
);

参数详解:

  1. lpEnumFunc (Window Enumeration Procedure)

    c语言EnumWindows
    (图片来源网络,侵删)
    • 这是一个函数指针,指向一个由你自己定义的回调函数。
    • EnumWindows 每找到一个顶级窗口,就会调用一次这个回调函数,并将找到的窗口的句柄作为参数传递给它。
    • 回调函数的原型必须是固定的
      BOOL CALLBACK EnumWindowsProc(
        HWND hwnd,    // 找到的窗口的句柄
        LPARAM lParam // 你在 EnumWindows 中传入的 lParam
      );
      • HWND hwnd: 一个窗口句柄,代表当前被枚举到的窗口。
      • LPARAM lParam: 一个 32 位值,这是你在调用 EnumWindows 时传入的第二个参数,你可以用它来向回调函数传递自定义数据。
      • 返回值:
        • 如果回调函数返回 TRUEEnumWindows 会继续枚举下一个窗口。
        • 如果回调函数返回 FALSEEnumWindows 会立即停止枚举过程。
  2. lParam

    • 一个 32 位的参数,你可以向它传递任何你想要的数据(一个指向结构体的指针、一个整型值等)。
    • 这个值会原封不动地传递给你的回调函数 EnumWindowsProc 的第二个参数。
    • 如果你不需要向回调函数传递数据,可以简单地将其设置为 0

返回值:

  • TRUE: 枚举成功完成(或者被回调函数主动终止)。
  • FALSE: 枚举失败,你可以调用 GetLastError() 函数来获取具体的错误代码。

如何使用 EnumWindows (一个完整的示例)

下面是一个完整的 C 语言示例,它演示了如何使用 EnumWindows 来列出桌面所有顶级窗口的标题和类名。

步骤:

  1. 包含必要的头文件 (windows.h)。
  2. 定义回调函数 EnumWindowsProc
  3. main 函数中调用 EnumWindows,并将回调函数的地址作为参数传入。
  4. 在回调函数中,使用 GetWindowTextGetClassName 等函数来获取窗口信息并打印。

示例代码:

#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;
}

如何编译和运行:

  1. 将上述代码保存为 enum_windows.c
  2. 使用 Visual Studio 的开发者命令提示工具进行编译:
    cl enum_windows.c
  3. 运行生成的 enum_windows.exe,你将看到一个命令行窗口,列出了当前桌面上所有可见窗口的详细信息。

高级用法:查找特定窗口

EnumWindows 最常见的用途之一是查找一个满足特定条件的窗口(标题包含“记事本”的窗口)。

我们可以通过修改回调函数来实现这一点。

c语言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;
}

注意事项

  1. 线程安全:回调函数 EnumWindowsProc 是在 EnumWindows 内部创建的辅助线程上调用的,要避免在回调函数中与主线程共享未受保护的资源,如果需要共享数据,请使用同步机制(如互斥锁 Mutex)。
  2. 性能:如果桌面上有大量的窗口,EnumWindows 可能会花费一些时间,在回调函数中应避免执行耗时操作。
  3. Unicode 支持:现代 Windows 应用广泛使用 Unicode,为了编写兼容 ANSI 和 Unicode 的代码,推荐使用带 _t 前缀的通用 API 函数,_tcscpy_tcslenGetWindowTextLength 等。GetWindowText 会根据编译环境自动处理字符集。
  4. EnumChildWindows:如果你需要枚举一个特定窗口的子窗口,可以使用 EnumChildWindows 函数,它的用法和 EnumWindows 非常相似,只是它需要一个父窗口的句柄作为第一个参数。

EnumWindows 是 Windows 系统编程中一个功能强大且基础的工具,通过它,你可以:

  • 获取系统上所有窗口的列表
  • 查找特定属性(如标题、类名)的窗口
  • 对每个窗口执行操作(发送消息、修改样式、获取窗口文本等)。

掌握 EnumWindows 和回调函数的用法,是进行 Windows 桌面自动化、插件开发、系统监控等任务的关键一步。

-- 展开阅读全文 --
头像
织梦网站所有图片不显示
« 上一篇 01-30
dede arclist如何正确传递参数值?
下一篇 » 01-30

相关文章

取消
微信二维码
支付宝二维码

目录[+]