应先用enumprocesses获取pid列表,再以足够权限调用openprocess,配合queryfullprocessimagenamew等获取路径;模块枚举优先用enumprocessmodulesex,句柄枚举须依赖ntquerysysteminformation并动态解析结构体,且需分层按需处理以保障稳定性。

用 EnumProcesses 获取所有进程 ID,但别漏掉权限检查
Windows 下最直接的方式是调用 EnumProcesses,但它只返回 DWORD 类型的 PID 列表,不带进程名或路径。常见错误是直接调用后就去查名字,结果多数返回空或拒绝访问——因为默认进程句柄权限不足。
必须先用 OpenProcess 以 PROCESS_QUERY_LIMITED_INFORMATION(Win8+)或 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ(旧系统)打开目标进程,再调用 GetModuleBaseNameW 或 QueryFullProcessImageNameW 获取路径。注意:64 位程序枚举 32 位进程时,QueryFullProcessImageNameW 在部分 Win10 版本会失败,此时降级用 GetProcessImageFileNameW(返回 NT 路径,如 \Device\HarddiskVolume3\Windows\System32\notepad.exe),再用 QueryDosDeviceW 翻译盘符。
- 调用
EnumProcesses前,确保缓冲区足够大(通常先用 0 长度试探,再分配) - 对每个 PID 调用
OpenProcess后,立刻检查返回值是否为NULL;失败常见原因是权限不足(尤其系统进程)或进程已退出 - 不要在循环中反复调用
CloseHandle前忘记判空,否则可能触发未定义行为
遍历进程模块要用 EnumProcessModulesEx,不是 EnumProcessModules
EnumProcessModules 只能枚举 32 位模块列表,且无法区分 32/64 位模块混排情况;真正可靠的是 EnumProcessModulesEx,它支持 LIST_MODULES_ALL 标志,能同时列出用户态和内核态模块(需管理员权限 + SeDebugPrivilege)。
关键点在于:模块句柄(HMODULE)本身不包含路径信息,必须配合 GetModuleFileNameExW(旧)或 GetMappedFileNameW(推荐,更稳定)提取路径。后者返回的是设备路径,需用 \??\C:\path → C:\path 的方式清洗前缀。
直播话术巡检系统(Windows版) — 实时语音转写、关键词检测、场景自动切换、告警推送、大屏可视化。支持 Windows VB-Cable 虚拟声卡音频采集,阿里云 ASR 实时识别。一键启动脚本,自动安装环境。
-
EnumProcessModulesEx在 Windows 7+ 才可用,XP 用户得回退到EnumProcessModules+ 多次尝试 - 即使有管理员权限,某些受保护进程(如
csrss.exe、smss.exe)仍可能拒绝模块枚举,应跳过而非报错中断 - 模块基址(
lpBaseOfDll)可用于后续内存扫描,但注意 ASLR 导致每次加载地址不同
句柄枚举依赖 NtQuerySystemInformation,没有公开 API
Windows 没有提供标准 Win32 API 枚举任意进程的打开句柄。唯一可行路径是调用未文档化的 NtQuerySystemInformation,传入 SystemHandleInformation(Win7 及以前)或 SystemExtendedHandleInformation(Win8+,含句柄类型名)。这需要手动解析结构体,且极易因版本差异崩溃。
调用前必须启用 SeDebugPrivilege,否则绝大多数句柄条目会显示为 0x00000000 或被过滤。结构体字段偏移随系统版本浮动(例如 Win10 22H2 的 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX 比 Win8 多 8 字节),硬编码偏移=必崩。
- 使用
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation")动态获取函数指针,避免静态链接导致兼容问题 - 申请缓冲区时预留至少 64KB,句柄数可能上千;失败时检查
STATUS_INFO_LENGTH_MISMATCH并重试 - 拿到句柄后,不能直接
DuplicateHandle或CloseHandle—— 目标进程可能已退出,且跨进程操作句柄极容易蓝屏或权限拒绝
实际工程中建议分层处理,别强求“全量准确”
真实场景下,试图一次性拉取所有进程 + 所有模块 + 所有句柄,不仅慢(枚举句柄常耗时数百毫秒),而且极易因权限、竞态、结构变更失败。更稳妥的做法是按需分层:
- 进程列表用
EnumProcesses定期快照(1–2 秒间隔),仅记录 PID + 主模块路径 - 模块信息只在用户点击某进程后按需枚举,避免预加载浪费
- 句柄信息默认不查,除非用户明确触发“分析资源占用”,且提示“需管理员权限”
- 所有系统调用都套
__try/__except(SEH)捕获访问冲突,防止因某个坏句柄导致整个工具崩溃
最易被忽略的是:句柄表里大量是内核对象(Event、Section、Key),它们没有文件路径,只有 ObjectTypeName 和 ObjectName(后者常为空)。强行转字符串只会得到乱码或触发异常。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










