Toolhelp API 简介
Toolhelp APIs是Windows中一组能够方便得到系统中win32应用程序的当前运行状况的函数,这些函数设计目的是实现一些基于Win32子系统的工具,特别是调试器。通过使用Toolhelp API,我们可以实现象Windows附带的系统工具Dr watson一样的功能(当然真正实现它的所有功能不是光靠Toolhelp API一个函数库就可以的)。而且这些函数适用于win9x,winnt,win2k,所以不需要为在不同的windows版本中移植代码操心。
下面将介绍ToolHelp APIs中一些主要的函数和结构。主要有CreateToolhelp32Snapshot和Xxx32First,Xxx32Next系列函数,还有Toolhelp32ReadProcessMemory。所用语言为Object Pascal,对于使用C++的程序员可以参考SDK index中的tool help library [Win32].
以下是它们的定义和说明:
1. 创建快照函数CreateToolhelp32Snapshot
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, // 要得到进程的信息,赋为TH32CS_SNAPPROCESS
// 要得到线程的信息,赋为TH32CS_SNAPTHREAD
// 要得到指定进程的堆列表,赋为TH32CS_SNAPHEAPLIST
// 要得到指定进程的模块列表,赋为TH32CS_SNAPMODULE
DWORD th32ProcessID // 当我们把dwFlags赋为TH32CS_SNAPMODULE 或者
// TH32CS_SNAPHEAPLIST时,需要指定具体的进程ID,否则
// 将忽略此参数。把它赋为0表示当前进程(也就是自己)
);// 返回值THandle将在以后要枚举进程、线程等对象时使用
当我们得到快照的句柄后可以分别使用Xxx32First,Xxx32Next这些函数进行对象枚举。
它们的定义如下所示:
2. Xxx32First,Xxx32Next枚举函数与有关结构
l 进程有关的函数与结构
typedef struct tagPROCESSENTRY32 {
DWORD dwSize; // 指定结构的大小,
// 在调用Process32First前需要设置,否则将会失败
DWORD cntUsage; // 引用计数
DWORD th32ProcessID; // 进程号
DWORD th32DefaultHeapID; // 进程的堆号,只对Toolhelp函数有意义,并不是一个有效句柄
DWORD th32ModuleID; // 进程的模块号,只对Toolhelp函数有意义,并不是一个有效句柄
DWORD cntThreads; // 进程中包括的线程数
DWORD th32ParentProcessID; // 进程的母进程
LONG pcPriClassBase; // 进程的线程的基优先级,所有此进程创建的线程将基于此优先级
DWORD dwFlags; // 保留,没有使用
char szExeFile[MAX_PATH]; // 进程所对应的文件及路径
} PROCESSENTRY32;
typedef PROCESSENTRY32 * PPROCESSENTRY32;
typedef PROCESSENTRY32 * LPPROCESSENTRY32;
BOOL WINAPI Process32First(
hSnapshot: THandle; // 前面由CreateToolhelp32Snapshot创建的快照句柄
var lppe: TProcessEntry32 //函数输出进程信息
): BOOL; //调用成功将为True,否则为False
BOOL WINAPI Process32Next(hSnapshot: THandle; var lppe: TProcessEntry32): BOOL;
l 模块有关的函数和结构
typedef struct tagMODULEENTRY32 {
DWORD dwSize; // 指定结构的大小,
// 在调用Module32First前需要设置,否则将会失败
DWORD th32ModuleID; // 模块号
DWORD th32ProcessID; // 包含本模块的进程号
DWORD GlblcntUsage; // 本模块的全局引用计数
DWORD ProccntUsage; // 包含模块的进程上下文中的模块引用计数
BYTE * modBaseAddr; // 模块基地址
DWORD modBaseSize; // 模块大小(字节数)
HMODULE hModule; // 包含模块的进程上下文中的hModule句柄.注意:modBaseAddr 与 hModule 只在th32ProcessID的上下文才有效
char szModule[MAX_MODULE_NAME32 + 1]; //模块名
char szExePath[MAX_PATH]; //模块对应的文件名和路径
} MODULEENTRY32;
typedef MODULEENTRY32 * PMODULEENTRY32;
typedef MODULEENTRY32 * LPMODULEENTRY32;
BOOL WINAPI Module32First(hSnapshot: THandle; var lpme: TModuleEntry32): BOOL; //同进程说明
BOOL WINAPI Module32Next(hSnapshot: THandle; var lpme: TModuleEntry32): BOOL;
l 线程有关的函数和结构
typedef struct tagTHREADENTRY32{
DWORD dwSize; // 指定结构的大小,
// 在调用Thread32First前需要设置,否则将会失败
DWORD cntUsage; // 线程引用计数
DWORD th32ThreadID; // 线程号
DWORD th32OwnerProcessID; // 拥有线程的进程号
LONG tpBasePri; // 在线程创建时的初始优先级
LONG tpDeltaPri; // 现在线程的优先级的相对于初始值的改变量
DWORD dwFlags; // 保留,没有使用
} THREADENTRY32;
typedef THREADENTRY32 * PTHREADENTRY32;
typedef THREADENTRY32 * LPTHREADENTRY32;
BOOL WINAPI Thread32First(hSnapshot: THandle; var lpte: TThreadEntry32): BOOL; stdcall;
BOOL WINAPI Thread32Next(hSnapshot: THandle; var lpte: TThreadENtry32): BOOL; stdcall;
l 堆有关的函数和结构
typedef struct tagHEAPENTRY32
{
DWORD dwSize; // 指定结构的大小,在调用Heap32First前需要设置,否则将会失败
HANDLE hHandle; // 堆的句柄
DWORD dwAddress; // 堆起始位置的线性地址
DWORD dwBlockSize; // 堆的大小(字节数)
DWORD dwFlags; // 标志,为以下值:LF32_FIXED 堆内存块的位置是固定的;LF32_FREE 堆内存块没有使用;LF32_MOVEABLE
//堆内存块的位置是可移动的
DWORD dwLockCount; // 堆的锁定计数,每次对堆的执行GlobalLock. 或者LocalLock都将使它增1
DWORD dwResvd; // 保留,没有使用
DWORD th32ProcessID; // 拥有本堆的进程号,这个ID是可以被其他WIN32 API使用的。
DWORD th32HeapID; // 堆号,只能在ToolHelp API中内部使用,
} HEAPENTRY32;
typedef HEAPENTRY32 * PHEAPENTRY32;
typedef HEAPENTRY32 * LPHEAPENTRY32;
BOOL WINAPI Heap32First(
LPHEAPENTRY32 lphe,
DWORD th32ProcessID,
DWORD th32HeapID
);
BOOL WINAPI Heap32Next(
LPHEAPENTRY32 lphe
);
3. 更为有用的Toolhelp API函数有Toolhelp32ReadProcessMemory,它可以读其他进程内存空间的内容。
BOOL WINAPI Toolhelp32ReadProcessMemory(
DWORD th32ProcessID, // 指定进程
LPCVOID lpBaseAddress, // 要读的进程内存空间的起始地址
LPVOID lpBuffer, // 将要读的内容的保存缓冲区
DWORD cbRead, // 要读的字节数
LPDWORD lpNumberOfBytesRead // 成功读取的字节数
); // 函数成功返回True,否则返回False
利用上面的函数,我们得出以下的程序端:
首先我们应该调用CreateToolhelp32Snapshot创建快照;然后调用Process32First,把创建的快照的句柄传递给hSnapshot参数,函数成功返回后我们将在lppe参数中得到第一个进程的信息;继续调用Process32Next,我们将得到后续的进程信息,直到Process32Next返回False为止。记住最后我们应该调用CloseHandle把快照的句柄安全的释放。 根据进程名找到进程ID,然后使用EnumWindows进行获取句柄。
EnumWindows函数功能:该函数枚举所有屏幕上的顶层窗口,办法是先将句柄传给每一个窗口,然后再传送给应用程序定义的回调函数。EnumThreadWindows函数继续到所有顶层窗口枚举完为止或回调函数返回FALSE为止函数原型:
BOOL EnumWindows(WNDENUMPROC lpEnumFunc,LPARAM lParam);
参数:
lpEnumFunc:指向一个应用程序定义的回调数指针,请参看EnumWindowsProc。
lPararm:指定一个传递给回调函数的应用程序定义值。
返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。
// 列出所有进程
void CProcess::SysListProcess()
{
// 初始化
HANDLE hProcessSnap = NULL;
PROCESSENTRY32 pe32 = {0};
// 获得句柄
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == (HANDLE)-1)
{
printf("\nCreateToolhelp32Snapshot()failed:%d",GetLastError());
return;
}
pe32.dwSize = sizeof(PROCESSENTRY32);
// 列举所有进程名称
if (Process32First(hProcessSnap, &pe32))
{
for (int i = 0; Process32Next(hProcessSnap, &pe32); i++)
{
// 将进程名加到列表中
m_list.InsertItem(i, pe32.szExeFile); // CListCtrl m_list;
}
}
else
{
printf("\nProcess32Firstt() failed:%d",GetLastError());
}
// 关闭句柄
CloseHandle (hProcessSnap);
}