windows 上查系统全局句柄总数需调用 ntquerysysteminformation + systemhandleinformation(或 win10+ 的 systemextendedhandleinformation),获取快照中 numberofhandles 字段值,该值即当前所有进程句柄总数,但必须以管理员权限运行并启用 sedebugprivilege。

Windows 上怎么查进程的全局句柄总数
Windows 没有公开的、跨版本稳定的 API 直接返回“系统全局句柄总数”,但每个进程的句柄计数是可查的,而系统级统计本质是所有进程句柄数之和(加上内核保留的少量句柄)。真正能用的入口是 NtQuerySystemInformation,配合 SystemHandleInformation(或 Win10+ 的 SystemExtendedHandleInformation)——它返回当前所有进程打开的句柄快照。
注意:这不是实时流式接口,而是快照;且需要 SeDebugPrivilege 权限,否则调用会失败并返回 STATUS_ACCESS_DENIED。
- 必须以管理员权限运行程序,否则
NtQuerySystemInformation返回错误码0xC0000022 -
SystemHandleInformation在 Windows 10 1607+ 已被标记为 deprecated,推荐优先用SystemExtendedHandleInformation(结构多一个ObjectTypeNameOffset和ObjectNameOffset字段) - 返回数据无序、无索引,需遍历全部句柄并按
UniqueProcessId分组统计
用 NtQuerySystemInformation 获取句柄快照的最小可行代码
绕过 ntdll.lib 链接依赖,直接 GetProcAddress 加载函数更稳妥。以下片段只做句柄总数统计(不解析类型名),适配 Win7–Win11:
// 注意:需 #include <winternl.h>,并手动声明
typedef NTSTATUS (NTAPI *pNtQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
pNtQuerySystemInformation NtQuerySystemInformation =
(pNtQuerySystemInformation)GetProcAddress(hNtDll, "NtQuerySystemInformation");
ULONG size = 0;
NTSTATUS status = NtQuerySystemInformation(SystemHandleInformation, nullptr, 0, &size);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
auto buf = std::make_unique<byte>(size);
status = NtQuerySystemInformation(SystemHandleInformation, buf.get(), size, &size);
if (NT_SUCCESS(status)) {
PSYSTEM_HANDLE_INFORMATION handles = (PSYSTEM_HANDLE_INFORMATION)buf.get();
DWORD total = handles->NumberOfHandles; // 这就是当前系统所有句柄总数(快照)
printf("Total handles: %u\n", total);
}
}
</byte></winternl.h>
关键点:NumberOfHandles 是结构体第一个字段,代表本次快照捕获到的句柄总条目数,不是某个进程的。它已包含所有进程(含系统进程如 svchost.exe、wininit.exe)的句柄。
为什么 GetProcessHandleCount 不够用
GetProcessHandleCount 只返回**当前进程**的句柄数,完全无法反映系统负载。很多人误以为调用它再遍历所有进程就能凑出全局总数,但这是错的:
- 你无法用
OpenProcess打开某些保护进程(如csrss.exe、smss.exe),GetProcessHandleCount会失败 - 即使能打开,句柄数在两次调用间可能变化,导致漏计或重复计
- 内核对象(如节、互斥体)的句柄可能被多个进程共享,简单相加会高估真实资源占用
所以必须用 NtQuerySystemInformation —— 它由内核一次性枚举,原子性强,且包含所有可访问句柄(只要权限到位)。
监控时容易忽略的性能与稳定性陷阱
高频调用 NtQuerySystemInformation 做实时监控非常危险:
- 每次调用都会触发内核遍历所有句柄表,CPU 开销随系统句柄总数线性增长;一台句柄超 50 万的服务器上,单次调用可能耗时 10–30ms
- 返回缓冲区大小不固定,旧版 Windows 可能因缓冲区不足返回
STATUS_BUFFER_OVERFLOW,需重试逻辑(而新版倾向用STATUS_INFO_LENGTH_MISMATCH) - 结构体定义未公开,微软不保证二进制兼容;例如
SystemExtendedHandleInformation在 Win11 22H2 后新增字段,硬编码偏移会崩溃
实际部署建议:采样间隔不低于 3 秒,缓存上次结果用于 delta 计算;若只需告警而非绘图,用 WMI 的 Win32_PerfFormattedData_PerfOS_Handle 类更轻量(但它只提供近似值,且延迟约 15 秒)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











