不能。GetProcessHandleCount仅返回当前进程句柄数,Windows无公开API提供全局句柄总数;近似方案是需提权调用未文档化NtQuerySystemInformation(SystemHandleInformation)获取系统所有句柄引用快照。

GetProcessHandleCount 能否直接拿到全局句柄数?
不能。GetProcessHandleCount 只返回**当前进程**的句柄总数,不是系统全局。Windows 没有公开 API 直接暴露“全系统句柄总数”,这是有意设计——避免应用依赖不稳定的全局统计,也防止信息泄露或滥用。
如果你在任务管理器里看到“句柄数”列,它显示的是单个进程的句柄计数,背后调用的正是 GetProcessHandleCount(或更底层的 NtQueryInformationProcess with ProcessHandleCount 类型)。
常见误操作:
- 把所有进程的 GetProcessHandleCount 结果相加 → 错误:部分句柄是内核对象跨进程共享的,重复计数;
- 尝试读取 \BaseNamedObjects 或 \KernelObjects 命名空间 → 权限受限、不可靠、不反映实际引用计数。
用 NtQuerySystemInformation 获取近似全局句柄总量
唯一可行的近似方案是调用未文档化但长期稳定的 NT API:NtQuerySystemInformation,传入 SystemHandleInformation(值为 16)。它返回系统中所有句柄的快照,包括句柄值、所属进程 ID、对象类型、访问掩码等。
注意:
- 需要 SeDebugPrivilege 权限(普通用户进程默认无权调用);
- 返回结构体格式随 Windows 版本微调(Win10/11 使用 SYSTEM_HANDLE_INFORMATION_EX 更稳定);
- 数据量大(几万条很常见),遍历耗时,不适合高频采样(如每秒多次)。
简短示意(省略权限提升和错误检查):
立即学习“C++免费学习笔记(深入)”;
NTSTATUS status = NtQuerySystemInformation(
SystemHandleInformation,
buffer, size, &returnSize
);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
// realloc buffer and retry
}
// 解析 buffer 中的 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX 数组
// 累加有效条目数 → 即为当前系统句柄总引用数(近似)
为什么 Process Explorer 显示的“句柄数”比你算的多?
因为 Windows 内部对同一对象的多个引用(比如一个文件句柄被线程 A 和线程 B 同时持有)会生成**独立的句柄表项**,而 NtQuerySystemInformation 返回的就是这些表项数量——它统计的是“句柄引用数”,不是“被打开的对象数”。
这正是你监控时真正该关心的指标:高句柄引用数往往意味着资源泄漏或未关闭的 CloseHandle 调用。
典型陷阱:
- 忽略 Object Type 字段,把事件、互斥体、节等全当“文件句柄”分析;
- 在 32 位程序中用 ULONG 解析 64 位 Windows 的 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX → 偏移错乱、崩溃;
- 没做 AdjustTokenPrivileges 提权就调用 NtQuerySystemInformation → 返回 STATUS_ACCESS_DENIED。
生产环境建议:用 ETW + Kernel Trace 替代轮询
如果目标是长期监控(比如服务健康检查),硬轮询 NtQuerySystemInformation 不仅性能差,还会干扰系统调度。更稳妥的方式是启用 ETW(Event Tracing for Windows)内核事件流,订阅 Microsoft-Windows-Kernel-Process 和 Microsoft-Windows-Kernel-Object 提供的句柄创建/关闭事件。
优势:
- 无提权要求(需管理员启动 session);
- 低开销、事件驱动,不主动扫描;
- 可关联进程、线程、堆栈(配合 StackWalk64)定位泄漏源头。
工具链上,logman + tracerpt 可导出原始数据,C++ 中用 TraceSession API 实时消费更灵活。但要注意:ETW 本身不提供“当前总数”,需自行维护计数器(初始化时仍需一次 NtQuerySystemInformation 快照作基准)。
最常被忽略的一点:句柄泄漏不一定出现在你自己的代码里——第三方 DLL、驱动、甚至 .NET 的 SafeHandle 实现异常都可能导致计数异常增长。监控必须覆盖整个进程生命周期,而非只盯住 CreateFile / CreateEvent 调用点。


















