如何仅用WinAPI查询指定进程的私有工作集大小?
不用WMI,纯WinAPI获取进程私有工作集大小
嘿,我完全理解你想绕开WMI、只用原生WinAPI拿到进程私有工作集大小的需求——WMI有时候确实显得笨重又麻烦。你提到GetProcessMemoryInfo()只能拿到总工作集,那咱们来拆解一下用VirtualQueryEx结合QueryWorkingSetEx手动计算的可行方案,其实逻辑没你想的那么复杂,只是需要分步骤梳理清楚。
先明确概念:私有工作集是什么?
私有工作集(Working Set - Private)指的是进程当前加载到物理内存中、且仅被该进程独占使用的内存页总量,和任务管理器里显示的那个数值对应。它和GetProcessMemoryInfo()返回的PrivateUsage(私有字节)不是一回事——后者是进程所有私有虚拟内存的大小,包括不在物理内存里的部分。
核心思路:遍历工作集+判断私有页
要拿到这个值,我们需要两步走:
- 获取进程当前的所有工作集页(也就是正在占用物理内存的页)
- 对每个页判断是否属于私有内存,最后统计总大小
具体实现步骤&代码示例
1. 获取目标进程的权限句柄
首先得拿到目标进程的句柄,必须申请足够的权限:
#include <windows.h> #include <psapi.h> #pragma comment(lib, "psapi.lib") SIZE_T GetPrivateWorkingSetSize(DWORD dwProcessId) { // 申请必要权限:查询进程信息+读取进程内存 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId); if (hProcess == NULL) { return 0; // 处理错误,比如权限不足 }
2. 获取工作集信息
用QueryWorkingSetEx()获取整个工作集的页数据,这个函数会返回每个工作集页的虚拟地址和属性:
DWORD dwBufferSize = 0; // 先调用一次获取所需缓冲区大小 QueryWorkingSetEx(hProcess, NULL, dwBufferSize); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CloseHandle(hProcess); return 0; } // 分配缓冲区存储工作集信息 PWS_EX_INFORMATION pWsInfo = (PWS_EX_INFORMATION)malloc(dwBufferSize); if (pWsInfo == NULL) { CloseHandle(hProcess); return 0; } if (!QueryWorkingSetEx(hProcess, pWsInfo, dwBufferSize)) { free(pWsInfo); CloseHandle(hProcess); return 0; }
3. 统计私有工作集大小
遍历每个工作集页,通过WS_EXFLAG_PRIVATE标志直接判断是否为私有页(Vista及以上系统支持),旧系统则用VirtualQueryEx()判断内存类型:
SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); SIZE_T pageSize = sysInfo.dwPageSize; // 获取系统页大小(通常4KB) SIZE_T privateWorkingSet = 0; DWORD dwPageCount = dwBufferSize / sizeof(WS_EX_INFORMATION); for (DWORD i = 0; i < dwPageCount; i++) { LPVOID pageAddr = (LPVOID)pWsInfo[i].VirtualAddress; // Vista+系统直接用WS_EXFLAG_PRIVATE标志判断,最准确 if (pWsInfo[i].Flags & WS_EXFLAG_PRIVATE) { privateWorkingSet += pageSize; continue; } // 兼容XP及更早系统,用VirtualQueryEx判断内存区域类型 MEMORY_BASIC_INFORMATION mbi; if (VirtualQueryEx(hProcess, pageAddr, &mbi, sizeof(mbi))) { // 私有内存区域直接计入 if (mbi.Type == MEM_PRIVATE) { privateWorkingSet += pageSize; } // 非共享的映射内存也属于私有工作集 else if (mbi.Type == MEM_MAPPED && !(mbi.Protect & (PAGE_READWRITE_SHARED | PAGE_EXECUTE_READWRITE_SHARED))) { privateWorkingSet += pageSize; } } }
4. 清理资源并返回结果
free(pWsInfo); CloseHandle(hProcess); return privateWorkingSet; }
关键注意事项
- 权限问题:必须确保调用
OpenProcess时申请了PROCESS_QUERY_INFORMATION和PROCESS_VM_READ权限,否则后续函数会调用失败。 - 系统兼容性:
WS_EXFLAG_PRIVATE是Vista及以后才有的标志,XP系统只能通过内存区域类型判断,准确性稍差但可以接受。 - 效率问题:遍历整个工作集和内存区域会有一定开销,但针对单个进程来说,这个开销在大多数场景下是可以忽略的。
内容的提问来源于stack exchange,提问作者Krisztián Szabó
相关产品推荐
相关产品推荐

