列举 Windows 所有进程(ToolHelp)
引子
由于这阵子必须得做几个小东西才行,估计着呢,是要做个
但是在功能上呢,又必须得比
说实在的,在
而我这里说的强大一点呢,并不是说要在功能上比
而是在仿照
比如在内存的管理上,Windows
所以在这里就可以加上一个功能,比如可以实现,当用户选定一个进程后,
我可以采用
当然上面的功能可能只是要实现的一个部分而已,至于其他的功能还有待分析才能够确定,
因为本来要做的这个东西也并不是工作上所需,只是个人所要求而已,所以什么需求之类的都没有,
只是想到什么做什么而已,呵呵,所谓一切随意 ~~~
因为要做的是
比如有很多东西都不能够直接在用户层上获取到,而必须深入到内核层才能够获取到这些信息,
比如内存的具体分配,甚至是关键性进程的终止等等,这些都必须在内核层才能实现,
而这很明显得通过驱动程序来实现,而在用户层的话自然就一个界面而已,对于这个界面呢,可以随意采用什么做,
Java
对于
唯一的遗憾就是奶奶的内存耗得有点过分,如果我的
而
所以对于这个任务管理器的实现呢,基本思路还是比较明确的,
首先通过在应用程序(VC
而后在每个时间间隔里发送命令给驱动程序(WDM),
然后通过驱动程序执行命令后再和应用程序通信,这样就可以将得到的数据传递给用户程序,
然后用户程序就可以利用最新获取的数据来更新用户界面了。
这里的发送的命令呢,基本上就两种,
一种为查询命令,该命令用来实现查询功能,比如指定进程的内存消耗等,
然后还有一个呢就是执行命令,比如终止一个进程,
其实上面的这个查询内存以及终止进程功能在用户模式下也是可以实现的,
但是相对于在内核模式下而言,用户模式下所做的操作是非常有限的,
而在用户层的话,很明显,是需要显示所有的进程信息的,
并且在和驱动层进行通信的时候,需要考虑传递用户选定的进程的句柄,
所以在用户层是需要保存有所有的进程的句柄的,为了简单起见,
这里可以不通过驱动来实现进程信息的获取,而是通过其他的方式来获取。
本篇博文的一个介绍点就是获取到系统当前状态下所有的进程等信息
(这些信息包括进程信息,进程使用的模块信息,进程所拥有的线程信息)。
ToolHelp
ToolHelp
呵呵,因为不多,所以我等下全部把它们贴出来咯,
首先呢,ToolHelp
C:\Program Files\Microsoft SDKs\Windows\v7.0A\include\tlhelp32.h
当我们在使用的时候,需要包括头文件
在
然后就是来一块一块的介绍了。
快照 (Snapshot)解析
至于这个快照嘛,呵呵,解释起来还算是有点麻烦,
但是可以这样理解,快照就是给当前的系统所处的状态拍了张照片,
那么自然,这张照片里面就存放了当前系统在拍照那会儿所处的状态,这就是快照了。
所以如果要访问系统的当前状态,只需要给它拍一张快照就可以进行访问了。
拍快照的实现函数(CreateToolhelp32Snapshot
// The th32ProcessID argument is only used
// if TH32CS_SNAPHEAPLIST or TH32CS_SNAPMODULE is specified.
// th32ProcessID == 0 means the current process.
// NOTE that all of the snapshots are global except for the heap and module
// lists which are process specific. To enumerate the heap or module
// state for all WIN32 processes call with TH32CS_SNAPALL and the
// current process. Then for each process in the TH32CS_SNAPPROCESS
// list that isn't the current process, do a call with just
// TH32CS_SNAPHEAPLIST and/or TH32CS_SNAPMODULE.
HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);
// dwFlags
#define TH32CS_SNAPHEAPLIST 0x00000001
#define TH32CS_SNAPPROCESS 0x00000002
#define TH32CS_SNAPTHREAD 0x00000004
#define TH32CS_SNAPMODULE 0x00000008
#define TH32CS_SNAPMODULE32 0x00000010
#define TH32CS_INHERIT 0x80000000
#define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST |
TH32CS_SNAPPROCESS |
TH32CS_SNAPTHREAD |
TH32CS_SNAPMODULE)
拍快照那是很显然的,但是如果我们要给系统当前状态拍个全景的话,那么系统当前状态的信息就多呢,
而如果我们只要进程的信息,而你拍了个全景,里面有线程信息,有模块信息,有堆信息,
这不明摆着是浪费嘛,所以自然得有个快照类型,而这个快照类型就是由参数
这个参数的取值也在上面列出来了,都还比较好理解,这里就不做解释了,
反正在这里只需要记住一条,只要我需要系统的状态信息,我就给它拍快照,
需要进程信息就拍进程快照,需要线程信息就拍线程快照就可以了。
并且在拍完快照不需要使用快照了之后需要关闭快照句柄,
在
但是笔者在这个文件中没有发现这个函数,所以只能调用
而且
还有注意一点就是前面的英文注释中有一条,即如果
Heap Walking 解析
typedef struct tagHEAPLIST32
{
SIZE_T dwSize;
DWORD th32ProcessID; // owning process
ULONG_PTR th32HeapID; // heap (in owning process's context!)
DWORD dwFlags;
} HEAPLIST32;
typedef HEAPLIST32 * PHEAPLIST32;
typedef HEAPLIST32 * LPHEAPLIST32;
// dwFlags
#define HF32_DEFAULT 1 // process's default heap
#define HF32_SHARED 2 // is shared heap
上面的这个
下面就来解释
- dwSize:
HEAPLIST32 结构体的大小, 在使用 Heap32ListFirst 之前需要将这个成员设置好 。 - th32ProcessID:
这个参数代表一个进程的 ID,即进程标识符 。 - th32HeapID:
这个参数代表堆标识符 。 - dwFlags:
取值参考上面的代码 。
在使用当中呢,其实上面的这个结构体只需要填充第一个字段
其他字段都是通过调用函数
typedef struct tagHEAPENTRY32
{
SIZE_T dwSize;
HANDLE hHandle; // Handle of this heap block
ULONG_PTR dwAddress; // Linear address of start of block
SIZE_T dwBlockSize; // Size of block in bytes
DWORD dwFlags;
DWORD dwLockCount;
DWORD dwResvd;
DWORD th32ProcessID; // owning process
ULONG_PTR th32HeapID; // heap block is in
} HEAPENTRY32;
typedef HEAPENTRY32 * PHEAPENTRY32;
typedef HEAPENTRY32 * LPHEAPENTRY32;
// dwFlags
#define LF32_FREE 0x00000002
#define LF32_FIXED 0x00000001
#define LF32_MOVEABLE 0x00000004
//LF32_FIXED The memory block has a fixed (unmovable) location.
//LF32_FREE The memory block is not used.
//LF32_MOVEABLE The memory block location can be moved.
下面就来解释
- dwSize:
HEAPENTRY32 结构体的大小, 在使用 Heap32First 之前需要将这个成员设置好 。 - hHandle:
这个参数指向一个堆块 。 - dwAddress:
这个参数代表堆块的线性起始地址 。 - dwBlockSize:
当前这个堆块的大小 。 - dwFlags:
取值参考上面的代码 。 - dwLockCount:
该参数已不再使用,不管它 。 - dwResvd:
该参数也已不再使用 。 - th32ProcessID:
该参数即代表使用这个堆块的进程 ID 。 -
-
th32HeapID:
当前这个堆块的标识符。
在使用当中呢,其实上面的这个结构体只需要填充第一个字段
其他字段都是通过调用函数
然后我们就来看属于
BOOL WINAPI Heap32ListFirst(HANDLE hSnapshot, LPHEAPLIST32 lphl);
BOOL WINAPI Heap32ListNext(HANDLE hSnapshot, LPHEAPLIST32 lphl);
BOOL WINAPI Heap32First(LPHEAPENTRY32 lphe, DWORD th32ProcessID, ULONG_PTR th32HeapID);
BOOL WINAPI Heap32Next(LPHEAPENTRY32 lphe);
BOOL WINAPI Toolhelp32ReadProcessMemory(
DWORD th32ProcessID,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T cbRead,
SIZE_T *lpNumberOfBytesRead
);
Heap Walking 使用
#include
#include
#include
#include
using namespace std;
//获取指定进程下的堆信息
BOOL ListProcessHeaps(DWORD dwOwnerPID);
int main()
{
ListProcessHeaps(GetCurrentProcessId());
cout<<endl<<endl;
system("pause");
}
//获取进程堆信息
BOOL ListProcessHeaps(DWORD dwOwnerPID)
{
HEAPLIST32 hl;
HANDLE hHeapSnap = INVALID_HANDLE_VALUE;
//创建指定进程下的堆快照
hHeapSnap = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, dwOwnerPID);
if (hHeapSnap == INVALID_HANDLE_VALUE)
{
return false;
}
//填充结构成员
hl.dwSize = sizeof(HEAPLIST32);
if(Heap32ListFirst(hHeapSnap, &hl))
{
do
{
//堆中的一个块
HEAPENTRY32 he;
ZeroMemory(&he, sizeof(HEAPENTRY32));
he.dwSize = sizeof(HEAPENTRY32);
//遍历当前进程,指定堆 ID 下的所有块
if(Heap32First(&he, GetCurrentProcessId(), hl.th32HeapID))
{
printf("\nHeap ID: %d\n", hl.th32HeapID);
do
{
printf("Block size: %d\n", he.dwBlockSize);
he.dwSize = sizeof(HEAPENTRY32);
} while(Heap32Next(&he));
}
hl.dwSize = sizeof(HEAPLIST32);
} while (Heap32ListNext(hHeapSnap, &hl));
}
CloseHandle(hHeapSnap);
return true;
}
Process Walking 解析
下面贴出的就是在
我还是会分为一部分一部分的贴出,这样比较好介绍。
typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID; // this process
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID; // associated exe
DWORD cntThreads;
DWORD th32ParentProcessID; // this process's parent process
LONG pcPriClassBase; // Base priority of process's threads
DWORD dwFlags;
CHAR szExeFile[MAX_PATH]; // Path
} PROCESSENTRY32;
typedef PROCESSENTRY32 * PPROCESSENTRY32;
typedef PROCESSENTRY32 * LPPROCESSENTRY32;
下面就来解释
- dwSize:
PROCESSENTRY32 结构体的大小, 在使用 Process32First 之前需要将这个成员设置好。 - cntUsage:
这个参数呢,不管它了,也不需要设置,因为这个值的结果必须为 1 。 - th32ProcessID:
这个参数就很明显了,代表一个进程的 ID,即进程标识符。 - th32DefaultHeapID:
这个参数的话代表了进程的默认堆标识符。 - th32ModuleID:
这个参数代表了进程的模块标识符。 - cntThreads:
代表进程所启动的线程数目。 - th32ParentProcessID:
代表该进程的父进程 ID。 - pcPriClassBase:
该值代表由该进程所创建的线程所拥有的基本优先级。 - dwFlags:
保留,暂未使用。 - szExeFile:
返回该进程的可执行文件所在的路径。 - th32MemoryBase:
该可执行文件所加载的基地址。 - th32AccessKey:
访问标识符。
在使用当中呢,其实上面的这个结构体只需要填充第一个字段
其他字段都是通过调用函数
然后我们就来看属于
不过这里只是贴出
BOOL WINAPI Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
BOOL WINAPI Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
可以看到,其实真的就只有这么点东西而已,其中要使用的也就两个
Process Walking 使用
对于这个使用嘛,个人觉得直接把代码贴出来,然后附上注释,附上截图就 OK 了,
所以在这里也还是以这种方式来介绍。
#include
#include
#include
#include
using namespace std;
//获取系统当前的所有进程
BOOL GetProcessList();
int main()
{
GetProcessList();
cout<<endl<<endl;
system("pause");
}
//获取到进程列表
BOOL GetProcessList()
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
//对系统中当前所有的进程拍下快照
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
//在使用 PROCESSENTRY32 结构之间需要先设置好该结构的大小
pe32.dwSize = sizeof(PROCESSENTRY32);
//获取第一个进程
if(!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap);
return FALSE;
}
//采用 Do - While 遍历所有进程
do
{
printf("\n-----------------------------------------------------");
printf("\n PROCESS NAME: = %s", pe32.szExeFile);
printf("\n parent process ID = 0xX", pe32.th32ParentProcessID);
printf("\n process ID = 0xX", pe32.th32ProcessID);
printf("\n thread count = %d", pe32.cntThreads);
printf("\n Priority Base = %d", pe32.pcPriClassBase);
//遍历获取下一个进程
} while(Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return TRUE;
}
Thread Walking 解析
typedef struct tagTHREADENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID; // this thread
DWORD th32OwnerProcessID; // Process this thread is associated with
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32;
typedef THREADENTRY32 * PTHREADENTRY32;
typedef THREADENTRY32 * LPTHREADENTRY32;
下面就来解释
- dwSize:
PTHREADENTRY32 结构体的大小, 在使用 Thread32First 之前需要将这个成员设置好。 - cntUsage:
这个参数不管它了,也不需要设置,总是为 0 。 - th32ThreadID:
这个参数代表一个线程的 ID,即线程标识符。 - th32OwnerProcessID:
这个参数的话代表了该线程所属的进程的标识符。 - tpBasePri
: 这个参数代表了分配给这个线程的基本优先级。 -
-
tpDeltaPri :
这个参数总是 0 ,不需要理会 。 -
-
dwFlags :
这个参数也总是 0 ,不需要理会 。
在使用当中呢,其实上面的这个结构体只需要填充第一个字段
其他字段都是通过调用函数
然后我们就来看属于
BOOL WINAPI Thread32First(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
BOOL WINAPI Thread32Next(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
可以看到,同进程那一块一样,在线程这里也只有两个
这里顺便提一下,在使用这些
Thread Walking 使用
#include
#include
#include
#include
using namespace std;
//获取系统当前的所有进程
BOOL GetProcessList();
//获取当前进程下的所有的线程信息
BOOL ListProcessThreads(DWORD dwOwnerPID);
int main()
{
GetProcessList();
cout<<endl<<endl;
system("pause");
}
//获取到进程列表
BOOL GetProcessList()
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
//对系统中当前所有的进程拍下快照
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
//在使用 PROCESSENTRY32 结构之间需要先设置好该结构的大小
pe32.dwSize = sizeof(PROCESSENTRY32);
//获取第一个进程
if(!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap);
return FALSE;
}
//采用 Do - While 遍历所有进程
do
{
printf("\n-----------------------------------------------------");
printf("\n PROCESS NAME: = %s", pe32.szExeFile);
printf("\n parent process ID = 0xX", pe32.th32ParentProcessID);
printf("\n process ID = 0xX", pe32.th32ProcessID);
printf("\n thread count = %d", pe32.cntThreads);
printf("\n Priority Base = %d", pe32.pcPriClassBase);
//列举出指定进程下的所有线程
ListProcessThreads(pe32.th32ProcessID);
//遍历获取下一个进程
} while(Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return TRUE;
}
//获取指定进程下的所有的线程信息
BOOL ListProcessThreads(DWORD dwOwnerPID)
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
//给当前行的下所有的线程进行拍照
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if(hThreadSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
te32.dwSize = sizeof(THREADENTRY32 );
//获取指定进程的第一个线程
if(!Thread32First(hThreadSnap, &te32))
{
CloseHandle(hThreadSnap);
return( FALSE );
}
do
{
//用来核对当前线程是否属于指定进程
if(te32.th32OwnerProcessID == dwOwnerPID)
{
printf("\n\n THREAD ID = 0xX", te32.th32ThreadID);
printf("\n base priority = %d", te32.tpBasePri);
printf("\n delta priority = %d", te32.tpDeltaPri);
}
//遍历指定进程的下一个线程
} while(Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
return TRUE;
}
程序效果展示:
Module Walking 解析
typedef struct tagMODULEENTRY32
{
DWORD dwSize;
DWORD th32ModuleID; // This module
DWORD th32ProcessID; // owning process
DWORD GlblcntUsage; // Global usage count on the module
DWORD ProccntUsage; // Module usage count in th32ProcessID's context
BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
HMODULE hModule; // The hModule of this module in th32ProcessID's context
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH];
} MODULEENTRY32;
typedef MODULEENTRY32 * PMODULEENTRY32;
typedef MODULEENTRY32 * LPMODULEENTRY32;
下面就来解释
- dwSize:
PMODULEENTRY32 结构体的大小, 在使用 Module32First 之前需要将这个成员设置好 。 - th32ModuleID
: 这个参数已不再使用,不管它了,也不需要设置 。 - th32ProcessID:
这个参数就很明显了,代表一个进程的 ID,即进程标识符 。 - GlblcntUsage:
这个参数代表这个模块在整个系统中加载的数目 , 也不用理会 。 - ProccntUsage:
这个参数代表和前面的参数差不多,只不过不再是整个系统,而是指当前进程的上下文中,也不理会 。 - modBaseAddr:
代表模块在进程上下文中的基地址 。 - modBaseSize:
代表该模块的大小 。 - hModule
: 该值代表模块句柄 。 -
-
szModule :
该参数代表模块名。 - szExePath:
回该模块所在的路径。
在使用当中呢,其实上面的这个结构体只需要填充第一个字段
其他字段都是通过调用函数
然后我们就来看属于
BOOL WINAPI Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
BOOL WINAPI Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
Module Walking 使用
#include
#include
#include
#include
using namespace std;
//获取系统当前的所有进程
BOOL GetProcessList();
//获取指定进程所引用的模块信息
BOOL ListProcessModules(DWORD dwPID);
int main()
{
GetProcessList();
cout<<endl<<endl;
system("pause");
}
//获取到进程列表
BOOL GetProcessList()
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
//对系统中当前所有的进程拍下快照
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
//在使用 PROCESSENTRY32 结构之间需要先设置好该结构的大小
pe32.dwSize = sizeof(PROCESSENTRY32);
//获取第一个进程
if(!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap);
return FALSE;
}
//采用 Do - While 遍历所有进程
do
{
printf("\n-----------------------------------------------------");
printf("\n PROCESS NAME: = %s", pe32.szExeFile);
printf("\n parent process ID = 0xX", pe32.th32ParentProcessID);
printf("\n process ID = 0xX", pe32.th32ProcessID);
printf("\n thread count = %d", pe32.cntThreads);
printf("\n Priority Base = %d", pe32.pcPriClassBase);
//列出与该进程相关联的模块信息
ListProcessModules(pe32.th32ProcessID);
//遍历获取下一个进程
} while(Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return TRUE;
}
//获取指定进程引用的所有的模块信息
BOOL ListProcessModules(DWORD dwPID)
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
//给进程所引用的模块信息设定一个快照
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if(hModuleSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
me32.dwSize = sizeof(MODULEENTRY32);
if(!Module32First(hModuleSnap, &me32))
{
CloseHandle(hModuleSnap);
return FALSE;
}
do
{
printf("\n\n MODULE NAME: %s", me32.szModule);
printf("\n executable = %s", me32.szExePath);
printf("\n process ID = 0xX", me32.th32ProcessID);
printf("\n ref count (g) = 0xX", me32.GlblcntUsage);
printf("\n ref count (p) = 0xX", me32.ProccntUsage);
printf("\n base address = 0xX", (DWORD)me32.modBaseAddr);
printf("\n base size = %d", me32.modBaseSize);
} while(Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
return TRUE;
}
程序效果展示:
其实上面的这些
而在
从下面的截图中就可以看出这点。
总结
上面呢通过一个一个的
其实说白了也就是 12 个
通过
上面的代码呢,我也是通过
个人觉得还是蛮有参考价值的,有兴趣的可以留个纪念 。