GetProcessHandleCount 是 Windows 下获取当前进程内核对象句柄数的最直接 API,需传入 GetCurrentProcess(),返回 DWORD 类型值,不包含 GDI/USER 句柄。

Windows 下用 GetProcessHandleCount 获取句柄数最直接
在 Windows 平台,C++ 程序要查自己当前进程的句柄总数,GetProcessHandleCount 是唯一推荐的 API。它不依赖 WMI、PSAPI 或性能计数器,开销低、调用简单、无需额外权限。
注意:这个函数只返回「当前进程」的句柄数(即内核对象句柄,如文件、事件、互斥体、线程等),不包含 GDI 或 USER 对象(窗口、画笔等)。如果需要 GDI/USER 句柄数,得用 GetGuiResources 单独查。
- 必须传入当前进程的伪句柄
GetCurrentProcess(),不能传NULL或其他值 - 函数成功返回非零值,失败返回 0 —— 但失败极少发生,通常意味着进程已终止或严重损坏
- 返回值是
DWORD类型,不是int;建议用std::cout (count)输出避免符号扩展问题
#include <windows.h>
#include <iostream>
int main() {
DWORD count;
if (GetProcessHandleCount(GetCurrentProcess(), &count)) {
std::cout << "Handle count: " << count << "\n";
} else {
std::cerr << "GetProcessHandleCount failed, error: "
<< GetLastError() << "\n";
}
}
为什么不用 NtQueryInformationProcess?
有人会想到用 NT 内部 API NtQueryInformationProcess 配合 ProcessHandleCount 信息类,但它不是公开支持的接口,行为不稳定,且在不同 Windows 版本中可能返回错误值(比如 Windows 11 22H2+ 某些更新后该字段恒为 0)。
更关键的是:NtQueryInformationProcess 需要链接 ntdll.lib,而微软明确声明该 DLL 不受 ABI 兼容性保证 —— 一旦系统更新就可能崩。
立即学习“C++免费学习笔记(深入)”;
-
GetProcessHandleCount就是NtQueryInformationProcess的封装层,且经过充分测试和验证 - 如果你看到示例代码里硬调
NtQueryInformationProcess,基本是过时资料或逆向工程产物,不建议用于生产环境 - 即使你真想用,也必须用
GetProcAddress动态加载,不能静态链接
Linux/macOS 没有等价概念,别强行移植
POSIX 系统没有“进程句柄数”这个抽象。类似指标靠文件描述符(fd)数量近似,但 fd 和 Windows 句柄语义不同:fd 只覆盖 I/O 资源,不包括同步对象、注册表键、注册服务等。
所以 C++ 跨平台代码里,GetProcessHandleCount 必须用 #ifdef _WIN32 包裹,Linux 下要么跳过,要么用 getrlimit(RLIMIT_NOFILE, ...) + 扫描 /proc/self/fd/ 目录来估算 open fd 数 —— 但这和 Windows 的“句柄数”完全不可比。
- 不要试图用
sysconf(_SC_OPEN_MAX)替代,它返回的是上限,不是当前使用量 -
/proc/self/fd/在容器或 chroot 环境下可能不可读,需检查errno == EACCES - macOS 没有
/proc,得用proc_pidinfo(..., PROC_PIDLISTFDS, ...),接口更复杂且需libproc
容易被忽略的点:句柄泄漏检测不能只看总数
监控 GetProcessHandleCount 返回值上升趋势,确实能发现明显泄漏,但它掩盖了关键细节:泄漏可能集中在某类对象(比如重复 CreateEvent 但没 CloseHandle),而总数变化缓慢。
真正有效的排查,得结合:
- Windows Performance Recorder(WPR)录制 Handle 事件,用 WPA 分析具体类型和堆栈
- Application Verifier 开启
Handle检查项,会在泄漏发生时触发断点 - 代码中对每个
Create*/Open*调用,确保配对CloseHandle—— 注意INVALID_HANDLE_VALUE不可 Close,NULL句柄也不可 Close
句柄数本身只是表象,真正要盯住的是谁分配、谁释放、有没有跨线程传递后忘记清理。


















