4 进程控制
4.1 获得系统进程
使用toolhelp模块可以实现获取系统中当前运行当中的进程列表。
思路如下,使用CreateToolhelp32Snapshot
函数给当前系统内执行的进程拍快照(Snapshot),也就是获得了进程列表,这个列表记录着进程的ID、进程对应的可执行文件的名称和创建该进程的进程ID等数据。然后使用Process32First
函数和Process32Next
函数遍历快照中记录的列表。
#include <windows.h>
#include <TlHelp32.h> // for snapshot
#include <stdio.h>
int main(int argc, char* argv[])
{
PROCESSENTRY32 pe32;
int iProcessCount;
// initial its size
pe32.dwSize = sizeof(pe32);
iProcessCount = 0;
// create snapshot for all processes
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot failure!\n");
exit(1);
}
// traversal snapshot
BOOL bMore = ::Process32First(hProcessSnap, &pe32);
while (bMore)
{
printf("%-20.20s%6u%4u%6u%6u%6u%3ld\n", pe32.szExeFile,
pe32.th32ProcessID, pe32.cntUsage, pe32.th32DefaultHeapID,
pe32.th32ModuleID, pe32.th32ParentProcessID, pe32.pcPriClassBase);
bMore = ::Process32Next(hProcessSnap, &pe32);
iProcessCount ++;
}
// clear snapshot
::CloseHandle(hProcessSnap);
printf("Processes count: %d\n\n", iProcessCount);
getchar();
return 0;
}
结果很容易理解。
CreateToolhelp32Snapshot
用于获取系统内指定进程的快照,也可以获取这些进程使用的堆、模块、线程的快照。如:
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, // 指定快照内容
DWORD th32ProcessID // 指定进程ID
);
dwFlags
参数可以是如下值:
-
TH32CS_SNAPHEAPLIST
枚举`th32ProcessID参数指定进程中的堆。 -
TH32CS_SNAPMODULE
枚举`th32ProcessID参数指定进程中的模块。 -
TH32CS_SNAPPROCESS
,此时`th32ProcessID参数被忽略。 -
TH32CS_SNAPTHREAD
,此时`th32ProcessID参数被忽略。 -
TH32CS_SNAPALL
,相当于以上4个值的并集。
根据获取快照的不同,使用下面几组函数来获取快照信息:
-
Heap32ListFirst
Heap32ListNext
-
Module32First
Module32Next
-
Process32First
Process32Next
-
Thread32First
Thread32Next
-
Heap32First
Heap32Next
以上函数的第二个参数是指向如下之一结构的指针:
HEAPLIST32
HEAPENTRY32
MODULEENTRY32
THREADENTRY32
PROCESSENTRY32
typedef struct tagHEAPLIST32 {
DWORD dwSize;
DWORD th32ProcessID;
DWORD th32HeapID;
DWORD dwFlags;
} HEAPLIST32;
typedef struct tagHEAPENTRY32
{
DWORD dwSize;
HANDLE hHandle;
DWORD dwAddress;
DWORD dwBlockSize;
DWORD dwFlags;
DWORD dwLockCount;
DWORD dwResvd;
DWORD th32ProcessID;
DWORD th32HeapID;
} HEAPENTRY32;
typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
TCHAR szModule[MAX_MODULE_NAME32 + 1];
TCHAR szExePath[MAX_PATH];
DWORD dwFlags
} MODULEENTRY32;
typedef struct tagTHREADENTRY32{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
DWORD th32AccessKey;
DWORD th32CurrentProcessID;
} THREADENTRY32;
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
DWORD th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
DWORD th32MemoryBase;
DWORD th32AccessKey;
} PROCESSENTRY32;
4.2 终止当前进程
终止一个进程有如下4种方式:
- 主线程的入口函数返回。
- 进程中一个线程调用了
ExitProcess
函数。 - 此进程中的所有线程都结束了。
- 其他进程中的一个线程调用了
TerminateProcess
函数。
最常用的方式是让主线程的入口函数返回。当入口函数返回时,启动函数会调用C/C++运行期退出函数exit
,并将用户的返回值传递给它。exit
函数会销毁所有的全局的或者静态的C++对象,然后调用系统函数ExitProcess
使操作系统终止应用程序。ExitProcess
是一个API函数,它会结束当前应用程序的执行,并设置它的退出代码。
VOID ExitProcess(
UINT uExitCode // 所有线程的退出代码
);
在程序的任何地方都可以调用ExitProcess
,强制当前程序的执行立即结束。这对操作系统来说,是正常的。但对C/C++应用程序应该避免直接调用这个函数。因为这会使C/C++运行期库得不到通知,而没有机会去调用全局的或者静态的C++对象的析构函数。
4.3 终止其他进程
ExitProcess
函数只能用来结束当前进程,不能用于结束其他进程。如果要终止其他进程,可以使用TerminateProcess
函数。
BOOL TerminateProcess(
HANDLE hProcess, // 目标进程句柄
UINT uExitCode // 目标进程的退出代码
);
前面介绍过,使用CreateProcess
函数创建新的进程,会得到新进程的句柄。对于不是自己创建的进程,可以使用OpenProcess
函数来取得这进程的访问权限,函数如下:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问权限
BOOL bInheritHandle, // 返回的句柄是否可被继承
DWORD dwProcessId // 要打开的进程的ID
);
参数dwDesiredAccess
指定的对进程的访问权限,可以是如下值:
值 | 说明 |
---|---|
PROCESS_ALL_ACCESS | 所有可进行的权限 |
PROCESS_CREATE_PROCESS | 创建一个进程 |
PROCESS_CREATE_THREAD | 创建一个线程 |
PROCESS_DUP_HANDLE | 做为DuplicateHandle 参数 |
PROCESS_QUERY_INFORMATION | 查看进程信息的权限,如优先级类等 |
PROCESS_QUERY_LIMITED_INFORMATION | 需要返回特定信息 |
PROCESS_SET_INFORMATION | 设置进程的优先级 |
PROCESS_SET_QUOTA | 设置内存限制 |
PROCESS_SUSPEND_RESUME | 睡眠和唤醒 |
PROCESS_TERMINATE | 关闭进程 |
PROCESS_VM_OPERATION | 修改进程的地址空间 |
PROCESS_VM_READ | 读进程内存 |
PROCESS_VM_WRITE | 写进程内存 |
SYNCHRONIZE | 等待进程结束 |
在进程结束后,调用GetExitCodeProcess
函数可以取得其退出代码,如果调用时,目标进程还没有结束,此函数会返回STILL_ACTIVE
,表示进程还在运行。通过该函数可以检测进程是否结束了。
BOOL GetExitCodeProcess(
HANDLE hProcess, // 目标进程句柄
LPDWORD lpExitCode // 目标进程的退出句柄
);
如果进程已经结束,其退出代码是通过下面几种方式设置的:
-
ExitProcess
或者TerminateProcess
函数中指定。 -
main
或者WinMain
的返回值。 - 由于未处理的异常导致的结束,返回相应的异常值。
一旦进程终止,就会如下事件发生:
- 所有被这个进程创建的或撕开的对象句柄就会关闭。
- 此进程内的所有线程将终止执行。
- 进程内核对象变成受信状态,所有等待在此对象上的线程开始运行,即
WaitForSingleObject
函数返回。 - 系统将进程对象中退出代码值由
STILL_ACTIVE
设置为指定的退出代码。