前言:上一回分析了任务管理器中任务栏窗口的遍历及其一些操作,在接下来的一系列文章中将分析进程管理的一些详细信息及操作。先来熟悉下系统自带的任务管理器中进程管理页面的详细内容吧。
在进程管理页面,可以通过菜单中的选择列来筛选需要查看进程相关信息的列数。在这些列栏中,有几个重要的名词需要解释下,因为有些名词偏离了实际的意义。
CPU Time --从进程开始运行所占用的总时间,其中包括KernelTime和UserTime,可以通过GetProcessTime获取。
Memory Usage Delta --两次统计更新之间,内存使用量的变化。
Page Faults --应用程序请求的数据不在内存而需要从硬盘中读取的次数,从进程启动开始计算
Page Faults Delta --和Memory Usage Delta类似,都是两次更新数据的增量
Page Pool --进程占用的操作系统内存数,页面池是可以分配到硬盘的虚拟内存,页面池包括所有用户的内存和操作系统内存的一部分。
其他的一些名词就不需要解释了,都可以“顾名思义”。
现在看看遍历进程得到进程相关信息吧,遍历进程的方法实在是很多,我采用的是Jeffery的CToolHelp类,对Tlhelp32.h里的API进行的一些封装。
- #ifndef __ENUM_PROCESS_H__
- #define __ENUM_PROCESS_H__
- #include <string>
- #include <vector>
- //#include <Winternl.h>
- using namespace std;
- #ifdef _UNICODE
- #define string wstring
- #endif
- //typedef struct _THREAD_INFO
- //{
- // LARGE_INTEGER CreateTime;
- // DWORD dwUnknown1;
- // DWORD dwStartAddress;
- // DWORD StartEIP;
- // DWORD dwOwnerPID;
- // DWORD dwThreadId;
- // DWORD dwCurrentPriority;
- // DWORD dwBasePriority;
- // DWORD dwContextSwitches;
- // DWORD Unknown;
- // DWORD WaitReason;
- //
- //}THREADINFO, *PTHREADINFO;
- //
- //
- //typedef struct _PROCESS_INFO
- //{
- // DWORD dwOffset;
- // DWORD dwThreadsCount;
- // DWORD dwUnused1[6];
- // LARGE_INTEGER CreateTime;
- // LARGE_INTEGER UserTime;
- // LARGE_INTEGER KernelTime;
- // UNICODE_STRING ProcessName;
- //
- // DWORD dwBasePriority;
- // DWORD dwProcessID;
- // DWORD dwParentProcessId;
- // DWORD dwHandleCount;
- // DWORD dwUnused3[2];
- //
- // DWORD dwVirtualBytesPeak;
- // DWORD dwVirtualBytes;
- // ULONG dwPageFaults;
- // DWORD dwWorkingSetPeak;
- // DWORD dwWorkingSet;
- // DWORD dwQuotaPeakPagedPoolUsage;
- // DWORD dwQuotaPagedPoolUsage;
- // DWORD dwQuotaPeakNonPagedPoolUsage;
- // DWORD dwQuotaNonPagedPoolUsage;
- // DWORD dwPageFileUsage;
- // DWORD dwPageFileUsagePeak;
- //
- // DWORD dCommitCharge;
- // THREADINFO ThreadSysInfo[1];
- //
- //} PROCESSINFO, *PPROCESSINFO;
- namespace enumProcess
- {
- class ProcessInfo
- {
- public:
- HICON m_hIcon; // 进程图标
- DWORD m_dwID; // 进程ID
- string m_strProcessName; // 进程名字
- string m_strProcessID; // 进程ID
- string m_strCPUUsage; // 进程CPU使用率
- string m_strCPUTime; // 进程CPU使用时间
- string m_strMemUsage; // 内存使用率
- string m_strMemDelta; // 内存增量
- string m_strMemPeakUsage; // 内存使用峰值
- string m_strPageFaults; // 页面错误
- string m_strPageSize; // 虚拟内存使用率
- string m_strPagePoolUsage; // 页面池使用率
- string m_strPageNonPollUsage; // 非页面池使用率
- string m_strUserObject; // 用户对象
- string m_strGDIObject; // GDI对象
- string m_strSessionID; // Session ID
- string m_strHandleCount; // 句柄总数
- string m_strIORead; // IO执行读取数
- string m_strIOReadByte; // IO读取字节数
- string m_strIOWrite; // IO执行写入数
- string m_strIOWriteByte; // IO写入字节数
- string m_strIOOther; // IO执行其他数
- string m_strIOOtherByte; // IO其他字节数
- string m_strPriority; // 进程优先级
- string m_strUserName; // 进程关联用户名
- string m_strThreadCount; // 进程包含线程数
- string m_strProcessPath; // 进程执行路径
- };
- typedef vector<ProcessInfo> TvecProInfo;
- class EnumProcess
- {
- public:
- EnumProcess();
- EnumProcess(const EnumProcess &src);
- //EnumProcess &operator=(const EnumProcess &src);
- virtual ~EnumProcess();
- TvecProInfo &GetProcessInfo();
- void Clear(); // 清空
- void Init(); // 初始化
- void Enum(); // 遍历进程时间
- private:
- void GetProcessName(ProcessInfo &proInfo);
- void GetProcessID(ProcessInfo &proInfo,DWORD dwID);
- void GetCPUUsage(ProcessInfo &proInfo,DWORD dwID);
- void GetCPUTime(ProcessInfo &proInfo,DWORD dwID);
- void GetMemInfo(ProcessInfo &proInfo,DWORD dwID);
- void GetResourceObject(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessSessionID(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessHandleCount(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessIOInfo(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessPriority(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessUserName(ProcessInfo &proInfo,DWORD dwID);
- void GetThreadCount(ProcessInfo &proInfo,DWORD dwCount);
- void GetProcessPath(ProcessInfo &proInfo,DWORD dwID);
- void GetProcessIcon(ProcessInfo &proInfo,LPCTSTR lpszPath);
- private:
- void TranslateFilename(LPCTSTR szFilename, LPTSTR szWin32Name); // 转换路径帮助函数
- void FormatNumber(DWORD dwNumber, TCHAR *szNumber, TCHAR cSeparator, DWORD GroupLength);// 转换数据格式帮助函数
- public:
- TvecProInfo m_vecProInfo;
- }; // end of class
- }; // end of namespace
- #endif
从这个遍历进程信息的类头文件中,可以看出需要得到指定进程的信息,只要知道进程的ID,然后再通过API调用就可以得到信息。在这里,需要特别提一下NtQuerySystemInformation,这个函数功能十分强大,强大到超出你的想象~但是MSDN上用大大的红字提示我们
[NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]
所以,我还是决定放弃使用这个函数,相应的我只能通过各个API来组合达到目的了。在这里,只需要说明几个特别的地方就可以了。
进程的CPU使用率:计算进程CPU的使用率就是计算进程占用CPU的时间:
- CPUsage = 100 * CurrentProcessCPUsage / TotalProcessCPUsage;
- CurrentProcessCPUsage = CurrentProcess.KernelTime + CurrentProcess.UserTime
- TotalProcessCPUsage = TotalProcess.KernelTime + TotalProcess.UserTime
进程CPU使用时间是GetProcessTime中KernelTime加上UserTime得来的,同时需要转换成系统时间
进程的内存使用信息可以通过GetProcessMemoryInfo获取,其中结构PROCESS_MEMORY_COUNTERS提供了许多有用的信息,具体察看MSDN吧
进程的User Objects和GDI Objects是通过GetGuiResources来获取的
进程的IO信息是GetProcessIoCounters
进程的映像路径GetModuleFileNameEx,然后就可以通过这个路径获取进程的icon了SHGetFileInfo
进程的用户名是
- // 获取进程用户名
- void EnumProcess::GetProcessUserName(ProcessInfo &proInfo,DWORD dwID)
- {
- HANDLE hProcess =NULL;
- HANDLE hToken =NULL;
- BOOL bResult =FALSE;
- DWORD dwSize =0;
- static TCHAR szUserName[MAX_PATH] ={0};
- static TCHAR szDomain[MAX_PATH] ={0};
- DWORD dwDomainSize =MAX_PATH;
- DWORD dwNameSize =MAX_PATH;
- SID_NAME_USE SNU;
- PTOKEN_USER pTokenUser=NULL;
- __try
- {
- hProcess=::OpenProcess(PROCESS_QUERY_INFORMATION | TOKEN_ADJUST_PRIVILEGES,
- FALSE,dwID);
- if( hProcess==NULL )
- __leave;
- if( !::OpenProcessToken(hProcess,TOKEN_QUERY,&hToken) )
- {
- int nErr=::GetLastError();
- bResult = FALSE;
- __leave;
- }
- if( !::GetTokenInformation(hToken,TokenUser,pTokenUser,dwSize,&dwSize) )
- {
- if( ::GetLastError() != ERROR_INSUFFICIENT_BUFFER )
- {
- bResult = FALSE;
- __leave;
- }
- }
- pTokenUser = NULL;
- pTokenUser = (PTOKEN_USER)malloc(dwSize);
- if( pTokenUser == NULL )
- {
- bResult = FALSE;
- __leave;
- }
- if( !::GetTokenInformation(hToken,TokenUser,pTokenUser,dwSize,&dwSize) )
- {
- bResult = FALSE;
- __leave;
- }
- if( ::LookupAccountSid(NULL,pTokenUser->User.Sid,szUserName,&dwNameSize,szDomain,&dwDomainSize,&SNU) != 0 )
- {
- proInfo.m_strUserName+=_T("");
- proInfo.m_strUserName+=szDomain;
- proInfo.m_strUserName+=_T("//");
- proInfo.m_strUserName+=szUserName;
- }
- }
- __finally
- {
- if( hProcess!=NULL )
- ::CloseHandle(hProcess);
- if( hToken!=NULL )
- ::CloseHandle(hToken);
- if( pTokenUser!=NULL )
- free(pTokenUser);
- }
- }
其他好像没有什么难度了,只要知道相关的API就容易了。最后,附上参考了的文章链接吧,我觉得相当有用!
MS的官方介绍:http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/core/fneb_mon_oyjs.mspx?mfr=true
定制调试诊断工具和实用程序(这个也是MS的,不过这是翻译文档):http://www.vckbase.com/document/viewdoc/?id=1590