//给列表框添加列 m_lcProcList.InsertColumn( 0, "ID", LVCFMT_LEFT, 40 ); m_lcProcList.InsertColumn( 1, "名称", LVCFMT_LEFT, 100 ); m_lcProcList.InsertColumn( 2, "路径", LVCFMT_LEFT, 250 ); m_lcProcList.InsertColumn( 3, "优先级", LVCFMT_LEFT, 50 ); m_lcProcList.InsertColumn( 4, "创建时间", LVCFMT_LEFT, 85 ); m_lcProcList.InsertColumn( 5, "当前内存", LVCFMT_LEFT, 60 ); m_lcProcList.InsertColumn( 6, "内存峰值", LVCFMT_LEFT, 60 ); //设置扩展风格 DWORD dwStyle = m_lcProcList.GetExtendedStyle(); dwStyle |= LVS_EX_FULLROWSELECT; //选中某行使整行高亮 dwStyle |= LVS_EX_GRIDLINES; //网格线(只适用与report风格的listctrl) m_lcProcList.SetExtendedStyle(dwStyle); //设置扩展风格 SetDebugEnable();//设置当前进程的安全属性 |
其中SetDebugEnable();函数用来改变当前进程的优先级,使当前进程能够访问运行在核心态的受保护的进程的地址空间,只有这样才能获得所有进程的详细信息。该函数的代码如下:
/*************************设置当前进程的安全属性*****************/ void CProcessListDlg::SetDebugEnable() { HANDLE hToken; //access token associated with a process LUID sedebugnameValue; //address of locally unique identifier TOKEN_PRIVILEGES tkp; //TOKEN_PRIVILEGES structure DWORD dProcessID = GetCurrentProcessId(); //Get Current Process's ID HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,FALSE, dProcessID); //OpenProcessToken if(!OpenProcessToken( hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { AfxTrace("OpenProcessToken failed with error %d/n",GetLastError()); return; } //LookupPrivilegeValue if(!LookupPrivilegeValue(NULL,"SeDebugPrivilege",&sedebugnameValue)) { AfxTrace("LookupPrivilegeValue failed with error %d/n",GetLastError()); CloseHandle(hToken); return; } //Set tkp tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = sedebugnameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //AdjustTokenPrivileges if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) { AfxTrace("AdjustTokenPrivileges failed with error %d/n",GetLastError()); return; } CloseHandle(hProcess); CloseHandle(hToken); } |
//获取进程列表信息 void CProcessListDlg::OnGetProcList() { m_lcProcList.DeleteAllItems(); //清除现有信息 //获取系统进程 PROCESSENTRY32 pe; //进程信息结构 pe.dwSize=sizeof(PROCESSENTRY32); //设置大小 //获取所有进程和进程信息的映射 HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); Process32First(hSnapshot, &pe); //获取映射中的第一个进程
HANDLE hProcess; //打开进程返回的句柄 CString strProcPath; //进程路径信息 CString szProcID; //进程ID CString szProcPri; //进程基本优先级 FILETIME lpCreationTime; //进程创建时间 FILETIME lpExitTime; //进程退出时间 FILETIME lpKernelTime; //进程在核心模式运行时间 FILETIME lpUserTime; //进程在用户模式下运行时间 CString strCreationTime; //进程创建时间 char cpProcCount[255]; //进程数量 CString strWorkingSetSize; //当前内存 CString strPeakWorkingSetSize; //内存峰值 PROCESS_MEMORY_COUNTERS ppsmemCounters; //进程内存使用信息结构 int nRow=0; //列表中项的数目
//获取进程信息 do { strProcPath=GetProcPath(pe.th32ProcessID); //进程路径 hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID); //打开进程 //获取进程相关时间信息 if(GetProcessTimes(hProcess, &lpCreationTime, &lpExitTime, &lpKernelTime, &lpUserTime)!=0) //获取进程相关时间信息 { strCreationTime = FiletimeToCString(lpCreationTime,true); //进程创建时 //其它相关时间信息请参考msdn } //获取进程内存使用情况时间信息 if(GetProcessMemoryInfo(hProcess,&ppsmemCounters,sizeof(PROCESS_MEMORY_COUNTERS))!=0) { strWorkingSetSize.Format("%dk",(ppsmemCounters.PagefileUsage)/1024); //进程内存 strPeakWorkingSetSize.Format("%dk",(ppsmemCounters.PeakWorkingSetSize)/1024); //进程内存峰值 //其它内存使用信息请参考msdn } szProcID.Format("%d", pe.th32ProcessID); //获取进程ID szProcPri.Format("%d", pe.pcPriClassBase); //进程基本优先级 //其它进程信息请参考msdn //把数据加入到列表控件中 m_lcProcList.InsertItem(nRow, szProcID); //进程ID m_lcProcList.SetItemText(nRow, 1, pe.szExeFile); //进程名 m_lcProcList.SetItemText(nRow, 2, strProcPath); //进程路径 m_lcProcList.SetItemText(nRow, 3, szProcPri); //进程基本优先级 m_lcProcList.SetItemText(nRow, 4, strCreationTime); //进程创建时间 m_lcProcList.SetItemText(nRow, 5, strWorkingSetSize); //进程在当前内存 m_lcProcList.SetItemText(nRow, 6, strPeakWorkingSetSize); //进程内存峰值 nRow++; //行数加一 CloseHandle(hProcess); //关闭打开的句柄 } while(Process32Next(hSnapshot, &pe)); sprintf(cpProcCount,"%d",nRow); //格式化进程数 m_sProcCount.SetWindowText(cpProcCount); //显示进程数 m_sProcCount.UpdateWindow(); CloseHandle(hSnapshot); //关闭句柄 } |
在这里用tlhelp32库的CreateToolhelp32Snapshot函数来获取系统所有进程信息的一个影射,然后用Process32First函数来获取第一个进程信息并存放在PROCESSENTRY32结构pe中,PROCESSENTRY32结构包含了进程的一些基本信息,比如说进程名,进程ID,进程的基本优先级等,详细信息参考MSDN。
GetProcPath函数用来获取进程的路径信息,代码如下:
CString CProcessListDlg::GetProcPath(DWORD dPID) { HMODULE hModuleArray[1024]; //模块句柄数组 HANDLE hProcess; //进程句柄 DWORD cbNeeded; //模块数量 CString strProcPath; //模块路径 char szModuleName[MAX_PATH]; //模块路径 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dPID); //打开进程 if (hProcess==NULL) //错误退出,返回空 return strProcPath; //枚举进程相关模块 if(EnumProcessModules(hProcess, hModuleArray,sizeof(hModuleArray), &cbNeeded)!=0) { //获取模块名。第一个模块,即为打开的进程模块 if(GetModuleFileNameEx(hProcess,hModuleArray[0],szModuleName, sizeof(szModuleName))!=0) { strProcPath.Format("%s",szModuleName); //格式化模块名 } } CloseHandle(hProcess); //关闭进程句柄 return strProcPath; //返回模块名 } |
其中用到了OpenProcess函数,用来打开指定ID的进程以获取其信息。EnumProcessModules函数用来枚举当前进程所有相关的模块,其中第一个模块就是进程本身,再调用GetModuleFileNameEx函数获取模块的完整路径,这两个函数都是psapi库的函数。因为我们只是想获取当前进程的完整路径,所以只需要获取该进程相关模块中第一个模块也就是该进程本身的完整路径即可。
再来看button的响应函数。GetProcessTimes函数用来获取进程相关时间信息,比如启动时间,在核心态运行时间,在用户态的运行时间,结束时间等等。在例子中只获取了启动时间,因为获取的时间是FILETIME格式,所以需要转化为系统的时间格式。代码如下:
CString CProcessListDlg::FiletimeToCString(FILETIME lpFiletime, bool bLocal) { CString strTime; //时间字符串 FILETIME lpLocalTime; //本地时间 SYSTEMTIME lpSystemTime; //系统时间结构 if(bLocal==true) { FileTimeToLocalFileTime(&lpFiletime,&lpLocalTime); //转化为本地时间 FileTimeToSystemTime(&lpLocalTime,&lpSystemTime); //转化为系统时间 } else { FileTimeToSystemTime(&lpFiletime,&lpSystemTime); //转化为系统时间 } strTime.Format("%02d:%02d:%02d:%03d",lpSystemTime.wHour, lpSystemTime.wMinute,lpSystemTime.wSecond,lpSystemTime.wMilliseconds); //格式化 return strTime; }
|
在这里我使用了两个参数,第一个是需要转化的FILETIME格式时间,第二个是个bool变量,如果为真,则先转化为本地时间在转化为系统时间格式,否则直接转化为系统时间格式。大家知道,时间有市区一说,默认获取的是格林尼治时间,需要转化到本地市区才可以再进行格式转化。但是对于运行时间这种则不需要转化,否则会出错。所以在转化比如进程启动时间时需要先转化为本地时间,其它则不需要。
另外一个获取进程信息的函数是GetProcessMemoryInfo,这个函数也是psapi库的函数,用来获取进程的内存使用信息,比如用了多少页面文件等等。在例子中获取了进程当前内存的大小和峰值大小。这个函数msdn中有详细说明。
到此为止进程的一些基本信息获取就完成了。下面写了一个获取进程相关模块的函数,作为用户单击list control中某一行的响应函数,代码如下:
//鼠标单机列表框响应函数,枚举进程相关模块 void CProcessListDlg::OnClickProclist(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; m_lbDllList.ResetContent(); //清除现有内容 CString strPID; //进程ID HMODULE lphModule[1024]; //模块句柄数组 DWORD cbNeeded; //关联模块数量 DWORD nModules; //模块 HANDLE hProcess; //进程句柄 char szModuleName[MAX_PATH]; //模块名 DWORD pID=0; //进程ID if(pNMListView->iItem != -1) { strPID = m_lcProcList.GetItemText(pNMListView->iItem,0); //得到进程ID sscanf(strPID.GetBuffer(strPID.GetLength()),"%d",&pID); //转化为DWORD hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pID); //打开进程获得句柄 if(hProcess==NULL) //如果进程为空,退出 return; //枚举进程关联模块 if(EnumProcessModules(hProcess, lphModule, sizeof(lphModule), &cbNeeded)!=0) { nModules = cbNeeded / sizeof(lphModule[0]); //得到数组长度 for (DWORD j = 0; j < nModules; j++) { HMODULE hModule = lphModule[j]; //模块句柄 GetModuleFileNameEx(hProcess, hModule, szModuleName, sizeof(szModuleName)); //得到模块文件名 m_lbDllList.AddString(szModuleName); //加入列表框 m_lbDllList.UpdateWindow(); } } CloseHandle(hProcess); //关掉进程句柄 } *pResult = 0; } |
可以看出整个过程和刚才获取进程完整路径的过程差不多,也不多说了。
到此为止,获取进程基本信息的程序就算完成了,虽然还谈不上完美。有些缺憾的是没有找到获取进程占用CPU信息的函数,不知道哪位大虾知道。最后附上我的程序界面。