摘 要:本文用Windows的进程查找、进程中止等API函数实现了任意中止系统中正在运行的进程的功能,并对进程操作中所用到的数据结构作了简要介绍。
主题词:API函数 进程控制
Windows是一种抢占式多任务的操作系统,在Windows运行时按下Ctrl+Alt+Del时会出现关闭程序对话框,我们能在任何时候中止一个选定的程序,就是这种抢占式多任务特点的具体体现,现在我们就讨论一下这种机制的实现原理,并编程实现相同的功能。
Windows中引进了进程的概念,进程指的是程序的一次执行过程。在内存中,进程对应着一段程序及一个数据结构――进程序控制块,它用来描述进程的各种特性,并唯一地标识一个进程。与DOS的内存控制块相似,内存中的进程控制块被组织成为队列,如果我们能遍历这个队列,然后选中其中的一个进程,中止该进程,也就可以实现与关闭程序对话框一样的功能。Windows的各种编程语言都不直接提供这样的操作,只能通过调用Windows的API函数来实现。
有两个函数可用于查找系统中的进程,Process32First函数查找系统中的第一个进程,Process32Next用于查找其他进程,先调用一次Process32First,然后反复调用Process32Next,就可以找出系统中的所有进程,这两个函数说明在tlhelp32.h中,调用函数的文件中要打开这个头文件。函数的原型为: BOOL Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
BOOL Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
函数带有两个参数,这两个参数要在调用这两个函数之前生成,首先要用以下函数建立一个快照句柄,
HANDLE CreateToolhelp32Snapshot(DWORD dwFlags,DWORD th32ProcessID );
该函数有两个变量,dwFlags 是查找的类型, 它规定了即将要查找的对象,该变量常见的取是如下两种:,
TH32CS_SNAPPROCESS 进程查找 TH32CS_SNAPTHREAD 线程查找 在进行进程和线程查找时,th32ProcessID 参数置0即可。 然后要定义一个PROCESSENTRY32型变量,PROCESSENTRY32是一个结构类型,其具体内容在MSDN中可以查到,该结构相当于进程控制块,Process32First和Process32Next函数将查找到的进程的特性记录在这个变量中。结构中与本程序有关的内容如下:
typedef struct tagPROCESSENTRY32 { DWORD dwSize;// 该结构的长度
……
DWORD th32ProcessID;// 进程标识符
……
char szExeFile[MAX_PATH]; // 程序的路径及文件名
} PROCESSENTRY32;
typedef PROCESSENTRY32 * PPROCESSENTRY32; 如果能得到进程的句柄,可用函数 TerminateProcess 中止一个进程 , 该函数说明如下:
BOOL TerminateProcess(
HANDLE hProcess, // 进程的句柄
UINT uExitCode // 进程的退出码
);
但 PROCESSENTRY32中只有进程的标识符,所以还要通过如下函数取得进程的句柄:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 存取标志
BOOL bInheritHandle, // 是否使用继承的句柄
DWORD dwProcessId // 进程标识符
);
以上是与程序有关的准备知识,现在我们就可以用上述的函数构造一个能中止任意进程的程序了。具体操作和编程过程如下。
用AppWizard生成一个基于对话框的新的工程(在生成过程的第一步选Dialog Based),本例工程名起名为proclist ,对话框的标题改为“进程中止”;在对话框中加入“刷新”、“中止”两按键,并分别赋以标识符IDC_REFRESH、IDC_STOP, 并为“中止”按键建立变量名m_stop;加入一个ClistCtrl控件,适当调整其大小,命以变量m_list 。
在proclistDlg.cpp的中的#include "proclistDlg.h"后加上#include "tlhelp32.h"
在函数CProclistDlg::OnInitDialog()中加入如下斜体字所示的语句
BOOL CProclistDlg::OnInitDialog()
{ …………
m_list.InsertColumn(0," 程 序 名",LVCFMT_LEFT,300,0);//在列表空制控件中加入一列
m_stop.EnableWindow (FALSE); //使中止按钮无效
OnRefresh(); //刷新列表控制控件中的内容,列出系统中的所有进程
return TRUE;
}
用ClassWizard生成OnItemchangedList1函数,当用鼠标在列表控制控件中选中一行时,将发送LVN_ITEMCHANGED消息,该函数就是该消息的映射函数,将函数体作如下改变,以使得当有行被选中时,中止按钮会有效。
void CProclistDlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
{
m_stop.EnableWindow (TRUE);
}
OnRefresh() 函数是“刷新”按钮按下消息的映射函数,用于刷新列表控制控件中的内容,他遍历系统中所有的进程,并将其文件名列入列表中。
void CProclistDlg::OnRefresh()
{
HANDLE hProcessSnap = NULL;
PROCESSENTRY32 pe32 = {0};
m_list.DeleteAllItems ();// 删除列表中所有的进程名
hProcessSnap =(HANDLE)CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hProcessSnap, &pe32);// 查找第一个进程
do {
m_list.InsertItem(0,pe32.szExeFile); //将进程名加入列表
}
while (Process32Next(hProcessSnap, &pe32));
// 查找下一个进程,直到所有的进程处理完
CloseHandle (hProcessSnap);// 关闭句柄
}
OnStop() 函数是“中止”按键按下消息的映射函数。
void CProclistDlg::OnStop()
{ char ItemText[70],procname[50];
lstrcpy(procname, m_list.GetItemText(m_list.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED),0)); //得到当前选中的进程的程序名
wsprintf(ItemText," 真的要中止%s吗?",procname);
HANDLE hProcessSnap = NULL;
PROCESSENTRY32 pe32 = {0};
hProcessSnap =(HANDLE)CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hProcessSnap, &pe32); //搜索该进程
do {
if (strstr(pe32.szExeFile,procname)) // 找到,用以下语句中止该进程
{ if (MessageBox(ItemText," 警告",MB_YESNO)==IDYES)
if(TerminateProcess(OpenProcess(0,FALSE,pe32.th32ProcessID),0))
{ MessageBox(" 中止成功","提示");
OnRefresh();} //删除后立即刷新
else MessageBox(" 中止失败","提示");
break; }
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle (hProcessSnap);
m_stop.EnableWindow (FALSE);
}
该函数中从列表中得到进程的文件名,再用进程程序名搜索进程,取得进程的ID,然后中止进程。这样做的目的是为了节省代码,虽然用Clist来管理已搜索到的进程,可以免去二次查找进程的,但增加了对链表维护的代码。
运行这个程序,出现如下所示窗体,在列表中列出了系统中驻留的全部进程的文件名,选定一个程序,按“中止”按钮,就可以中止选定的进程,列表中的内容随之改变;运行一些程序,再按下“刷新“按钮,这些程序的文件名也会反映在列表中。
本程序所用硬件为PⅡ400 CPU、64M内存,在Windows 98操作系统、VC++6.0环境下实现了中止任意程序的功能。
|