ntquerysysteminformation 返回 status_invalid_info_class 主因是误用已禁用的 systemhandleinformation(24),应改用 systemextendedhandleinformation(64)并动态加载、提权后两次调用获取 handlecount 总数。

为什么 NtQuerySystemInformation 返回 STATUS_INVALID_INFO_CLASS
直接调用 NtQuerySystemInformation 查询句柄总数时,最常见错误是用了已废弃或未公开的 SystemHandleInformation(即 24)。Windows 10 1809+ 和 Server 2019 起,该信息类默认被禁用,即使你正确声明了 SYSTEM_HANDLE_INFORMATION 结构体,也会触发 STATUS_INVALID_INFO_CLASS。这不是权限问题,也不是函数没导出——而是系统策略层面限制了该接口的直接使用。
真正可用且稳定的替代方案是:SystemExtendedHandleInformation(64),它返回带进程名的句柄列表,结构体更宽但兼容性好;或者改用 SystemKernelDebuggerInformation 等间接方式(不推荐)。实战中应优先尝试 64。
- 必须用
ntdll.dll动态加载NtQuerySystemInformation,不能静态链接 - 调用前需启用
SE_DEBUG_PRIVILEGE权限,否则部分进程句柄无法枚举(尤其是系统进程) -
SystemExtendedHandleInformation返回的NumberOfHandles字段才是当前系统总句柄数,不是数组长度
如何安全获取 SYSTEM_HANDLE_INFORMATION_EX 并统计总数
Windows SDK 不提供 SYSTEM_HANDLE_INFORMATION_EX 定义,必须手动声明。注意:该结构体在不同 Windows 版本中字段对齐可能变化,不能直接按旧版 SYSTEM_HANDLE_INFORMATION 复用。
关键点在于两次调用模式:第一次传 nullptr 获取所需缓冲区大小,第二次分配足够内存再调用。失败时检查 STATUS_INFO_LENGTH_MISMATCH,而非直接报错退出。
- 缓冲区大小建议初始分配 64KB,多数场景够用;若失败则按返回的
*ReturnLength重新分配 -
HandleCount字段位于结构体开头,类型为ULONG,不是数组元素个数,而是系统级总计数 - 每个句柄条目(
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX)的ObjectTypeIndex可用于过滤(如只算File或Event类型)
// 示例片段:获取总数(省略权限提升和错误处理)
ULONG size = 0;
NTSTATUS status = pNtQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)64, nullptr, 0, &size);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
auto buf = std::make_unique<uchar>(size);
status = pNtQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)64, buf.get(), size, &size);
if (NT_SUCCESS(status)) {
auto info = reinterpret_cast<psystem_handle_information_ex>(buf.get());
printf("Total handles: %lu\n", info->HandleCount); // 这就是你要的总数
}
}</psystem_handle_information_ex></uchar>
用 GetProcessHandleCount 只能查单个进程,别误当系统总数
GetProcessHandleCount 是 Win32 API,只能返回**调用者自身进程**的句柄数(即当前程序打开了多少句柄),和系统全局句柄总数完全无关。很多开发者看到函数名里有 “HandleCount” 就以为是系统级接口,结果调试半天发现数值始终在几百徘徊——那只是你自己的进程。
- 它不需要提权,也不依赖 ntdll,但作用域严格限定为当前进程
- 返回值是
DWORD,但实际受进程句柄表限制(默认上限约 16,777,216,但极少达到) - 若需监控多个进程句柄数,必须用
OpenProcess+NtQuerySystemInformation枚举匹配 PID 的条目,不能靠累加
权限、符号与调试器干扰的真实影响
即使代码逻辑完全正确,仍可能拿不到准确总数:一是权限不足导致跳过某些进程(如 svchost、lsass),二是启用内核调试器(如 WinDbg 启用本地内核调试)会强制禁用 SystemExtendedHandleInformation,此时返回 STATUS_ACCESS_DENIED。
- 提权代码必须在调用前执行,且要确保
SeDebugPrivilege已启用(不是仅获取句柄) - 用
!handle命令在 WinDbg 中验证:若输出显示 “Scanning all processes...” 且无拒绝提示,说明权限到位 - Release 模式下关闭所有调试器连接,否则某些 Windows 版本会静默降级信息类
句柄总数本身是个瞬时快照,没有原子性保证;如果你在循环中反复调用并观察到剧烈波动(比如 ±5000),大概率是其他进程正在批量创建/关闭句柄,而不是你的代码有问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











