进程列表显示初探

        因为最近单位要做个项目,牵扯到一点查看系统进程的东西,所以查阅了很多资料,参考了很多前辈的代码,自己用VC++6.0做了个进程列表查看器,纯属学习之用,在这里顺便介绍一下查看进程的一些函数和用法。
        Windows公开的查看系统进程的API分别包含在两个库中,一个是 tlhelp32,一个是psapi,而两个库各有所长,并不是完全等同或者说谁的功能更强大一点。在这里就不一一列举两个库中的所有函数,结合自己的实例介绍一些相关的函数,大家要看详细的信息可以参考msdn,里边介绍的还是比较详细。
        首先在VC建立一个对话框工程,命名为ProcessList,在上边添加一个List Control,用于显示进程的信息,再添加一个List Box,显示每个进程关联的模块,再添加一个Static Text,显示进程的数目。添加个button,单击来显示进程列表。
        首先在对话框的 OnInitDialog()函数中返回代码前添加如下代码:

//给列表框添加列

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);

}

        当然这函数应该添加到当前类中。
        接下来是Button的响应函数,代码如下:

//获取进程列表信息

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信息的函数,不知道哪位大虾知道。最后附上我的程序界面。

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值