2008年(909)
分类:
2008-05-06 22:29:33
下载源代码
枚举顶层(top-level)窗口
枚举进程
用 ToolHelp32 库枚举进程
用 PSAPI 枚举进程
16位进程的处理方法
关于代码
参考资料
摘要
我们在编写程序时,常常遇到的一件事情就是要准确列出系统中所有正在运行的程序或者进程。Windows
任务管理器就是这样的一个程序。它既能列出运行的桌面应用程序,又能列出系统中所有运行的进程。那么,我们在程序中如何实现这样的任务呢?本文下面将详细讨论这个问题。
枚举顶层(top-level)窗口
枚举桌面顶层窗口相对于枚举进程来说可能要容易一些。枚举桌面顶层窗口的方法是用 EnumWindows() 函数。不要用
GetWindow()来创建窗口列表,因为窗口之间复杂的父子及同胞关系(Z-Order)容易造成混乱而使得枚举结果不准确。
EnumWindows()有两个参数,一个是指向回调函数的指针,一个是用户定义的 LPARAM 值,
针对每个桌面窗口(或者顶层窗口)它调用回调函数一次。然后回调函数用该窗口句柄做一些处理,比如将它添加到列表中。这个方法保证枚举结果不会被窗口复杂的层次关系搞乱,因此,一旦有了窗口句柄,我们就可以通过 GetWindowText()
得到窗口标题。
枚举进程
建立系统进程列表比枚举窗口稍微复杂一些。这主要是因为所用的 API 函数对于不同的 Win32 操作系统有依赖性。在 Windows
9x、Windows Me、Windows 2000 Professional 以及 Windows XP 中,我们可以用 ToolHelp32
库中的 APIs 函数。但是在 Windows NT 里,我们必须用 PSAPI 库中的 APIs 函数, PSAPI 库是 SDK
的一部分。本文我们将讨论上述所有平台中的实现。附带的例子程序将对上述库中的 APIs 进行包装,以便包装后的函数能支持所有 Win32
操作系统。
使用 ToolHelp32 库枚举进程
ToolHelp32
库函数在 KERNEL32.dll 中,它们都是标准的 API 函数。但是 Windows NT 4.0 不提供这些函。
ToolHelp32 库中有各种各样的函数可以用来枚举系统中的进程、线程以及获取内存和模块信息。其中枚举进程
只需用如下三个的函数:CreateToolhelp32Snapshot()、Process32First()和 Process32Next()。
使用 ToolHelp32 函数的第一步是用 CreateToolhelp32Snapshot()
函数创建系统信息“快照”。这个函数可以让你选择存储在快照中的信息类型。如果你只是对进程信息感兴趣,那么只要包含
TH32CS_SNAPPROCESS 标志即可。 CreateToolhelp32Snapshot() 函数返回一个
HANDLE,完成调用之后,必须将此 HANDLE 传给 CloseHandle()。
接下来是调用一次 Process32First 函数,从快照中获取进程列表,然后重复调用 Process32Next,直到函数返回
FALSE 为止。这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个
PROCESSENTRY32 结构。
调用完 Process32First 或 Process32Next 之后,PROCESSENTRY32
中将包含系统中某个进程的关键信息。其中进程 ID 就存储在此结构的 th32ProcessID。此 ID 可以被传给 OpenProcess()
API 以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile
结构成员中。在该结构中还可以找到其它一些有用的信息。
注意:在调用 Process32First() 之前,一定要记住将 PROCESSENTRY32 结构的 dwSize
成员设置成 sizeof(PROCESSENTRY32)。
使用 PSAPI 库枚举进程
在 Windows NT 中,创建进程列表使用 PSAPI 函数,这些函数在 PSAPI.DLL 中。这个文件是随 Platform SDK
一起分发的,最新版本的 Platform SDK 可以从这里下载:
使用这个库所需的 PSAPI.h 和 PSAPI.lib 文件也在该 Platform SDK 中。
为了使用 PSAPI 库中的函数,需将 PSAPI.lib 添加到代码项目中,同时在所有调用 PSAPI API 的模块中包含 PSAPI.h
文件。记住一定要随可执行文件一起分发 PSAPI.DLL,因为它不随 Windows NT 一起分发。你可以点击这里单独下载
PSAPI.DLL 的可分发版本(不用完全下载 Platform SDK)。
与 ToolHelp32 一样,PSAPI 库也包含各种各样有用的函数。由于篇幅所限,本文只讨论与枚举进程有关函数:EnumProcesses()、EnumProcessModules()、GetModuleFileNameEx()和 GetModuleBaseName()。
创建进程列表的第一步是调用 EnumProcesses()。该函数的声明如下:
BOOL EnumProcesses( DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded );EnumProcesses()带三个参数,DWORD 类型的数组指针 lpidProcess;该数组的大小尺寸 cb;以及一个指向 DWORD 的指针 cbNeeded,它接收返回数据的长度。DWORD 数组用于保存当前运行的进程 IDs。cbNeeded 返回数组所用的内存大小。下面算式可以得出返回了多少进程:nReturned = cbNeeded / sizeof(DWORD)。
EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );调用之后,hModule 变量中保存的将是进程中的第一个模块。记住进程其实没有名字,但进程的第一个模块既是该进程的可执行模块。现在你可以用 hModule 中返回的模块句柄调用 GetModuleFileNameEx() 或 GetModuleBaseName() API 函数获取全路径名,或者仅仅是进程可执行模块名。两个函数均带四个参数:进程句柄,模块句柄,返回名字的缓冲指针以及缓冲大小尺寸。
INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp,LPARAM lparam );
此处 dwProcessId 是 NTVDM 中拟枚举的16位任务进程标示符。参数 fp 是回调枚举函数的指针。参数 lparam
是用户定义的值,它被传递到枚举函数。枚举函数应该被定义成如下这样:
BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined );该函数针对每个运行在 NTVDM 进程中的16位任务调用一次,NTVDM 进程ID将被传入 VDMEnumTaskWOWEx()。如果想继续枚举则返回 FALSE,终止枚举则返回 TRUE。注意这是与 EnumWindows()相对的。
BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );
使用该函数时,要象下面这样声明回调函数:
BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );
参数 dw 包含 ID,“w16”是16位任务的任务号,如果为32位进程则为0(在 Windows 95 中总是0),参数lpstr
指向文件名,lParam 是用户定义的,要被传入 EnumProcs()。
EnumProcs() 函数通过显示链接使用 ToolHelp32 和 PSAPI,而非通常所用的隐式链接。之所以要这样做,主要是为了让代码能够在二进制一级兼容,从可以在所有
Win32 操作系统平台上运行。
参考资料