无法直接用 GetProcessHandleCount 查全局句柄数,因其仅返回当前进程的句柄数(读取 PEB->NumberOfHandles),不涉及其他进程或内核句柄表,且存在四舍五入、类型不可区分等问题。

Windows 系统没有公开、稳定、跨版本的 API 直接返回“全局句柄引用总数”,GetProcessHandleCount 只能查本进程,NtQuerySystemInformation 中的 SystemHandleInformation 虽然能枚举所有句柄,但代价极高且需管理员权限,不适用于高频监控。
为什么不能直接用 GetProcessHandleCount 查全局句柄数
GetProcessHandleCount 返回的是**当前调用进程**的打开句柄数,不是系统全局总数。它底层读取的是 PEB->NumberOfHandles,完全不涉及其他进程或内核句柄表。很多开发者误以为这是“系统句柄总数”,结果在监控服务中持续上报同一个小数字,完全失去意义。
- 调用前无需特权,但数据粒度太粗、场景错配
- 返回值是
DWORD,但实际可能被内核四舍五入(Win10 1903+ 某些版本会返回近似值) - 无法区分句柄类型(event、mutex、file、thread…),不利于归因分析
用 NtQuerySystemInformation + SystemHandleInformation 枚举全系统句柄(需提权)
这是目前唯一能在用户态接近“全局句柄总数”的方式,但必须以 SeDebugPrivilege 权限运行,且 SystemHandleInformation 结构体无文档保证,不同 Windows 版本字段偏移可能变化(如 Win11 22H2 已改用 SystemExtendedHandleInformation)。
- 调用前必须用
OpenProcessToken+AdjustTokenPrivileges启用SE_DEBUG_NAME - 返回的
SYSTEM_HANDLE_INFORMATION是变长数组,需循环解析:每个条目含ProcessId、Handle、ObjectTypeNumber、HandleAttributes、GrantedAccess - 统计总数只需累加条目数;按进程聚合需用
std::map<dword size_t></dword>计数;按类型分类需查ObjectTypeNumber → 名称映射表(常见值:2=file, 5=process, 6=thread, 27=event, 29=mutant) - 单次调用耗时约 5–50ms(取决于当前句柄总量,>10w 时明显卡顿),不可每秒调用
示例关键片段:
立即学习“C++免费学习笔记(深入)”;
NTSTATUS status = NtQuerySystemInformation(
SystemHandleInformation,
buffer, bufferSize, &bytesReturned
);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
// realloc buffer and retry
}
// 解析 buffer: (PSYSTEM_HANDLE_INFORMATION)(buffer + sizeof(ULONG))
更轻量替代方案:通过性能计数器(PerfCounter)间接获取
Windows 性能计数器提供 ObjectsHandles,它反映的是**内核对象句柄总数量**(非精确到引用计数,但趋势一致),无需提权,采样开销极低(微秒级),适合长期监控。
- 路径格式为
\<hostname>\Objects\Handles</hostname>,本地可用\localhost\Objects\Handles - 需链接
pdh.lib,用PdhOpenQuery/PdhAddCounter/PdhCollectQueryData - 返回值是
PDH_FMT_COUNTERVALUE,value.longValue即为估计总数(通常比NtQuerySystemInformation少 1–5%,因不包含某些内部伪句柄) - 注意:该计数器在 Server Core 或 Nano Server 上默认不可用;部分云主机(如 Azure Dv3 系列)因内核裁剪可能缺失
实战建议与易忽略点
真实监控系统中,不要只盯一个数字。句柄泄漏往往表现为“总数缓慢上涨 + 某几个进程句柄数异常高”。应组合使用:
- 每 30 秒采一次
\Objects\Handles做趋势报警(阈值建议设为 30000–50000,视服务器负载而定) - 每日凌晨低峰期用
NtQuerySystemInformation全量扫描,输出 top10 高句柄进程 + 各类型分布直方图 - 对疑似泄漏进程,用
GetProcessHandleCount+EnumProcessHandles(需 DbgHelp)做进程内细查,避免误杀系统服务 - 特别注意:Windows 10/11 的 Windows Defender 实时保护(
MsMpEng.exe)常驻 8k–12k 句柄,属正常行为,勿纳入告警白名单逻辑
最常被跳过的一步:没验证目标机器是否开启 SeDebugPrivilege 策略(组策略 → 安全设置 → 本地策略 → 用户权限分配),导致 NtQuerySystemInformation 静默失败返回 STATUS_ACCESS_DENIED,而日志里只写了“枚举失败”。


















