Windows下获取进程句柄数需调用未文档化的NtQuerySystemInformation,手动解析句柄表并过滤本进程;GetProcessHandleCount仅返回内核对象快照,不包含GDI/USER句柄且存在滞后与溢出风险。

Windows平台下用 NtQuerySystemInformation 获取进程句柄数
Windows 没有公开的 Win32 API 直接返回“当前进程句柄总数”,必须调用未文档化的 NT 内核函数 NtQuerySystemInformation,配合 SystemHandleInformation(或更安全的 SystemHandleInformationEx)来枚举所有句柄并过滤出本进程的。
关键点在于:该函数不是导出符号,需手动从 ntdll.dll 中获取地址;返回结构体格式随系统版本变化(如 Windows 10 1809+ 引入了 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX),硬编码偏移易崩溃。
- 必须用
GetModuleHandleA("ntdll.dll")+GetProcAddress加载NtQuerySystemInformation - 缓冲区大小需循环调用试探:第一次传
nullptr得到所需大小,再分配内存重试 - 遍历句柄表时,用
GetCurrentProcessId()对比每个UniqueProcessId字段 - 32 位程序在 64 位系统上运行时,若未启用 WOW64 兼容模式,可能读不到完整句柄表(需注意
IsWow64Process)
使用 GetProcessHandleCount 的局限性
Win32 提供了看似直接的 GetProcessHandleCount,但它只返回“内核对象句柄计数”的近似值,且仅适用于当前进程 —— 实际返回的是 EPROCESS 结构中 HandleCount 字段的快照,不包含用户态句柄(如 GDI、USER 句柄),也不反映句柄表碎片或已标记但未释放的项。
- 返回值是
DWORD,但某些高负载场景下可能溢出(虽罕见) - 它不区分句柄类型,也无法用于其他进程(参数只接受
GetCurrentProcess()) - 在 Windows Server 2016+ 和部分 Insider Build 中,该值有时滞后于真实句柄分配,尤其在多线程频繁 Create/Close 场景下
跨平台方案不可行,Linux/macOS 无等价概念
C++ 标准库和 POSIX 都不定义“进程句柄数”——Linux 用文件描述符(fd)替代,macOS 类似。所谓“句柄”是 Windows 特有抽象,GDI、USER、内核对象混用同一句柄表,而 Linux 的 /proc/[pid]/fd/ 只映射文件描述符,不包括窗口、菜单、DC 等 Win32 对象。
立即学习“C++免费学习笔记(深入)”;
- Linux 下可用
scandir("/proc/self/fd", ...)统计 fd 数,但这和 Windows 的GetProcessHandleCount语义不同 - macOS 没有用户态可访问的全局句柄表,
task_for_pid需要特权且被 SIP 严格限制,普通进程无法实现等效功能 - 试图封装跨平台接口只会掩盖差异,导致误判资源泄漏
调试阶段推荐用 Process Explorer 验证结果
自己写的枚举逻辑容易漏掉 ProtectedHandle 或因权限不足跳过某些句柄(如 SYSTEM 进程打开的句柄)。开发时务必用 Sysinternals 的 Process Explorer 对照验证:
- 右键目标进程 → Properties → “Handles” tab,顶部显示总数(含类型过滤)
- 对比你代码输出是否一致;若差几个,大概率是没处理
ObjectInfo中的Attributes标志位(如OBJ_PROTECTED_HANDLE) - 注意 Process Explorer 默认不显示内核驱动创建的句柄(需勾选 “Show kernel handles”)
最麻烦的其实是句柄生命周期管理:哪怕你数对了,也难判断哪个句柄该关——因为 CloseHandle 失败时不会告诉你它属于哪个对象,只能靠 RAII 或上下文追踪。


















