Windows无公开API直接获取系统句柄总数,需用NtQuerySystemInformation调用SystemHandleInformation(或SystemHandleInformationEx)枚举后统计,但须先启用SeDebugPrivilege权限;若仅查本进程句柄数,应使用轻量稳定的GetProcessHandleCount。

Windows下用NtQuerySystemInformation获取句柄总数
Windows没有公开的API直接返回“当前系统所有句柄总数”,但可通过NtQuerySystemInformation调用底层系统信息类SystemHandleInformation(或更稳定的SystemHandleInformationEx)枚举全部句柄,再统计数量。注意:这需要SeDebugPrivilege权限,普通进程默认无权执行。
常见错误是直接调用失败并报STATUS_ACCESS_DENIED(0xC0000022),本质不是函数写错,而是没提权。实操前必须先用OpenProcessToken + LookupPrivilegeValue + AdjustTokenPrivileges启用调试权限。
- 只在管理员权限启动的进程里尝试,否则提权会静默失败
-
SystemHandleInformation结构体无文档保证,不同Windows版本字段偏移可能变化;推荐用SystemHandleInformationEx(Win10 1809+)或退而求其次用NTDLL.DLL动态解析 - 返回的句柄列表包含所有进程(含内核进程如
System、smss.exe),总数通常在几万到十几万量级,别误以为是自己进程的
用GetProcessHandleCount快速查本进程句柄数
如果目标只是监控**当前进程**的句柄占用情况,完全不需要走NtQuery——GetProcessHandleCount是Windows提供的一等公民API,稳定、轻量、无需提权。
它返回的是当前进程内核对象句柄表中的有效条目数,和任务管理器“句柄数”列一致,且调用开销极低(微秒级)。但要注意它不反映跨进程共享句柄的引用计数变化,只统计本进程打开的句柄数量。
立即学习“C++免费学习笔记(深入)”;
- 调用前确保传入的是当前进程的有效句柄:
GetCurrentProcess()即可,不用OpenProcess - 返回值为
DWORD,不是BOOL,不要跟GetLastError混淆;失败时返回0,但0也可能是真实值(刚启动的空进程) - 该值不包括线程对象本身(线程句柄需显式
CreateThread并保留句柄才会计入),也不包括注册表键等伪句柄
PowerShell或命令行临时验证句柄总数
开发调试阶段想快速确认系统级句柄总量是否异常飙升,不必写C++程序,用PowerShell一行就能拿到近似值:
Get-Process | ForEach-Object { $_.HandleCount } | Measure-Object -Sum | Select-Object Sum
这本质是遍历所有进程调用Process.HandleCount(底层即GetProcessHandleCount),再累加。结果比NtQuerySystemInformation略低,因为它跳过了无访问权限的系统进程(如PID=4的System进程),但对日常监控足够可靠。
- PowerShell需以管理员身份运行才能看到全部进程,否则总和明显偏低
- 结果不含内核驱动创建的非进程关联句柄(如某些文件系统过滤器持有的句柄),这类极少,一般可忽略
- 命令执行有延迟(数百毫秒),不适合高频采样,仅用于人工核查
避免滥用NtQuerySystemInformation做高频监控
有人试图每秒调用NtQuerySystemInformation来绘制系统句柄趋势图,结果CPU飙升、进程卡死。根本原因是该API会遍历整个系统句柄表(常超10万项),且每次调用都要复制整块内存到用户态,成本远高于想象。
真正合理的做法是:用GetProcessHandleCount每秒查本进程;用PowerShell脚本每分钟跑一次全系统快照;若真需实时全局监控,应改用ETW(Event Tracing for Windows)订阅Microsoft-Windows-Kernel-Process事件里的句柄操作,而非轮询。
最易被忽略的一点:NtQuerySystemInformation返回的缓冲区大小不固定,必须先用0长度试探,再分配足够内存重试,两次调用间系统句柄数可能变化,导致第二次仍不足——得循环重试直到成功,否则直接崩溃。


















