列举 Windows 所有进程(ToolHelp)

引子

由于这阵子必须得做几个小东西才行,估计着呢,是要做个 Windows 的任务管理器出来才行,

但是在功能上呢,又必须得比 Windows 任务管理器强大一点,

说实在的,在 Windows 7 里面的 Windows 任务管理器在功能上已经很强大了,

而我这里说的强大一点呢,并不是说要在功能上比 Windows 7 的任务管理器还有多一些,

而是在仿照 Windows 任务管理器的同时实现一些 Windows 任务管理器还没有实现的功能,

比如在内存的管理上,Windows 任务管理器并没有针对于每一个进程均有对应的内存显示,

所以在这里就可以加上一个功能,比如可以实现,当用户选定一个进程后,

我可以采用 GDI 绘制出该进程所使用的内存曲线图,或者该进程内部的内存结构分布图等等,

当然上面的功能可能只是要实现的一个部分而已,至于其他的功能还有待分析才能够确定,

因为本来要做的这个东西也并不是工作上所需,只是个人所要求而已,所以什么需求之类的都没有,

只是想到什么做什么而已,呵呵,所谓一切随意 ~~~

因为要做的是 Windows 的任务管理器,而且还有一些特别的处理,所以在做法上自然有些特别,

比如有很多东西都不能够直接在用户层上获取到,而必须深入到内核层才能够获取到这些信息,

比如内存的具体分配,甚至是关键性进程的终止等等,这些都必须在内核层才能实现,

而这很明显得通过驱动程序来实现,而在用户层的话自然就一个界面而已,对于这个界面呢,可以随意采用什么做,

Java 我没试过怎么跟驱动程序通信,但是 C# VC 我都还是试过的,所以敲定在 VC C# 中选一个,

对于 C# 呢,说实在的,在 GDI+ 上绘图,我还是觉得很方便的,至少比起 GDI 是方便很多,

唯一的遗憾就是奶奶的内存耗得有点过分,如果我的 Timer 时间间隔比较小的话,总是感觉它会受不了,

GDI 的话貌似会好点,所以敲定使用 VC 来做用户界面,

所以对于这个任务管理器的实现呢,基本思路还是比较明确的,

首先通过在应用程序(VC 做的用户界面程序)中设置一个定时器,

而后在每个时间间隔里发送命令给驱动程序(WDM),

然后通过驱动程序执行命令后再和应用程序通信,这样就可以将得到的数据传递给用户程序,

然后用户程序就可以利用最新获取的数据来更新用户界面了。

这里的发送的命令呢,基本上就两种,

一种为查询命令,该命令用来实现查询功能,比如指定进程的内存消耗等,

然后还有一个呢就是执行命令,比如终止一个进程,

其实上面的这个查询内存以及终止进程功能在用户模式下也是可以实现的,

但是相对于在内核模式下而言,用户模式下所做的操作是非常有限的,

而在用户层的话,很明显,是需要显示所有的进程信息的,

并且在和驱动层进行通信的时候,需要考虑传递用户选定的进程的句柄,

所以在用户层是需要保存有所有的进程的句柄的,为了简单起见,

这里可以不通过驱动来实现进程信息的获取,而是通过其他的方式来获取。

本篇博文的一个介绍点就是获取到系统当前状态下所有的进程等信息

(这些信息包括进程信息,进程使用的模块信息,进程所拥有的线程信息)。

ToolHelp  API  概述              

ToolHelp  API  看起来貌似蛮多东西呢,其实呢,也就那么几个函数而已,

呵呵,因为不多,所以我等下全部把它们贴出来咯,

首先呢,ToolHelp 这些 API 所在的文件路径为:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\include\tlhelp32.h

当我们在使用的时候,需要包括头文件 <tlhelp32.h>,这样才能够引用到 ToolHelp API

ToolHelp API 中分为这么几个部分(参考下面的截图即可),

然后就是来一块一块的介绍了。

image

快照 (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)

拍快照那是很显然的,但是如果我们要给系统当前状态拍个全景的话,那么系统当前状态的信息就多呢,

而如果我们只要进程的信息,而你拍了个全景,里面有线程信息,有模块信息,有堆信息,

这不明摆着是浪费嘛,所以自然得有个快照类型,而这个快照类型就是由参数 dwFlags 来决定了。

这个参数的取值也在上面列出来了,都还比较好理解,这里就不做解释了,

反正在这里只需要记住一条,只要我需要系统的状态信息,我就给它拍快照,

需要进程信息就拍进程快照,需要线程信息就拍线程快照就可以了。

并且在拍完快照不需要使用快照了之后需要关闭快照句柄,

MSDN 里头说是在 TlHelp32.h 中有 CloseToolhelp32Snapshot 函数,

但是笔者在这个文件中没有发现这个函数,所以只能调用 CloseHandle 来实现关闭了,

而且 MSDN 里面貌似也是这样使用的,所以就不管他了。

还有注意一点就是前面的英文注释中有一条,即如果 th32ProcessID 传入 0 时代表当前进程。

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

        

上面的这个 HEAPLIST32 结构体描述了指定的进程所使用的堆链表的进入点,

下面就来解释 HEAPLIST32 中的结构成员的含义了。

  1. dwSize:                            HEAPLIST32 结构体的大小, 在使用 Heap32ListFirst 之前需要将这个成员设置好 。
  2. th32ProcessID:              这个参数代表一个进程的 ID,即进程标识符 。
  3. th32HeapID:                  这个参数代表堆标识符 。
  4. dwFlags:                         取值参考上面的代码 。

在使用当中呢,其实上面的这个结构体只需要填充第一个字段 dwSize 即可,

其他字段都是通过调用函数 Heap32ListFirst 或者是 Heap32ListNext 来填充的。

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.
                

下面就来解释 HEAPENTRY32 中的结构成员的含义了。

  1. dwSize:                            HEAPENTRY32 结构体的大小, 在使用 Heap32First 之前需要将这个成员设置好 。
  2. hHandle:                         这个参数指向一个堆块 。
  3. dwAddress:                    这个参数代表堆块的线性起始地址 。
  4. dwBlockSize:                 当前这个堆块的大小 。
  5. dwFlags:                         取值参考上面的代码 。
  6. dwLockCount:              该参数已不再使用,不管它 。
  7. dwResvd:                       该参数也已不再使用 。
  8. th32ProcessID:             该参数即代表使用这个堆块的进程 ID
  9. th32HeapID:                 当前这个堆块的标识符。

在使用当中呢,其实上面的这个结构体只需要填充第一个字段 dwSize 即可,

其他字段都是通过调用函数 Heap32First 或者是 Heap3Next 来填充的。

然后我们就来看属于 Heap Walking 这一块的 API 了。

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 <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
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;
}
程序效果展示:
可以看到 Test.exe 的父进程是 Dev-C++.exe,
这是因为笔者的这个 Demo 是通过 Dev-C++ 来编译运行的.
image
image
image

           

              

Process Walking 解析

 

下面贴出的就是在 TlHelp32.h  中的所有的关于进程这一块的代码了,

我还是会分为一部分一部分的贴出,这样比较好介绍。

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;

下面就来解释 PROCESSENTRY32 中的结构成员的含义了。

  1. dwSize:                            PROCESSENTRY32 结构体的大小, 在使用 Process32First 之前需要将这个成员设置好。
  2. cntUsage:                       这个参数呢,不管它了,也不需要设置,因为这个值的结果必须为 1 。
  3. th32ProcessID:              这个参数就很明显了,代表一个进程的 ID,即进程标识符。
  4. th32DefaultHeapID:    这个参数的话代表了进程的默认堆标识符。
  5. th32ModuleID:              这个参数代表了进程的模块标识符。
  6. cntThreads:                    代表进程所启动的线程数目。
  7. th32ParentProcessID: 代表该进程的父进程 ID
  8. pcPriClassBase:             该值代表由该进程所创建的线程所拥有的基本优先级。
  9. dwFlags:                         保留,暂未使用。
  10. szExeFile:                        返回该进程的可执行文件所在的路径。
  11. th32MemoryBase:       该可执行文件所加载的基地址。
  12. th32AccessKey:            访问标识符。

在使用当中呢,其实上面的这个结构体只需要填充第一个字段 dwSize 即可,

其他字段都是通过调用函数 Process32First 或者是 Process32Next 来填充的。

然后我们就来看属于 Process Walking 这一块的 API 了。

不过这里只是贴出 API 的原型,对于使用的话,放到代码中介绍来的更快。

BOOL WINAPI Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
 
BOOL WINAPI Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

可以看到,其实真的就只有这么点东西而已,其中要使用的也就两个 API 而已,简单吧~~~

Process Walking 使用

对于这个使用嘛,个人觉得直接把代码贴出来,然后附上注释,附上截图就 OK 了,

所以在这里也还是以这种方式来介绍。

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
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 = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", pe32.th32ProcessID);
        printf("\n  thread count      = %d", pe32.cntThreads);
        printf("\n  Priority Base     = %d", pe32.pcPriClassBase);
 
        //遍历获取下一个进程 
    } while(Process32Next(hProcessSnap, &pe32));
 
    CloseHandle(hProcessSnap);
    return TRUE;
}
         
程序效果展示:
可以看到 Test.exe 的父进程是 Dev-C++.exe,
这是因为笔者的这个 Demo 是通过 Dev-C++ 来编译运行的.
 
image

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;

下面就来解释 PTHREADENTRY32 中的结构成员的含义了。

  1. dwSize:                            PTHREADENTRY32 结构体的大小, 在使用 Thread32First 之前需要将这个成员设置好。
  2. cntUsage:                       这个参数不管它了,也不需要设置,总是为 0
  3. th32ThreadID:               这个参数代表一个线程的 ID,即线程标识符。
  4. th32OwnerProcessID这个参数的话代表了该线程所属的进程的标识符。
  5. tpBasePri :                      这个参数代表了分配给这个线程的基本优先级。
  6. tpDeltaPri :                     这个参数总是 0 ,不需要理会 。
  7. dwFlags :                         这个参数也总是 0 ,不需要理会 。

在使用当中呢,其实上面的这个结构体只需要填充第一个字段 dwSize 即可,

其他字段都是通过调用函数 Thread32First 或者是 Thread32Next 来填充的。

然后我们就来看属于 Thread Walking 这一块的 API 了。

BOOL WINAPI Thread32First(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
 
BOOL WINAPI Thread32Next(HANDLE hSnapshot, LPTHREADENTRY32 lpte);

可以看到,同进程那一块一样,在线程这里也只有两个 API

这里顺便提一下,在使用这些 API 的过程中都必须传递进去相对于的快照句柄。

Thread Walking 使用

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
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 = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", 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      = 0x%08X", te32.th32ThreadID);
            printf("\n     base priority  = %d", te32.tpBasePri);
            printf("\n     delta priority = %d", te32.tpDeltaPri);
        }
 
        //遍历指定进程的下一个线程 
    } while(Thread32Next(hThreadSnap, &te32)); 
 
    CloseHandle(hThreadSnap);
    return TRUE;
}

程序效果展示:

可以看到我们当前运行的这个 Test.exe 的进程中只有一个线程,
这肯定只有一个线程啦,因为我们在程序中就一个主线程,又没有创建其他线程。

image

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;

下面就来解释 PMODULEENTRY32 中的结构成员的含义了。

  1. dwSize:                            PMODULEENTRY32 结构体的大小, 在使用 Module32First 之前需要将这个成员设置好 。
  2. th32ModuleID :             这个参数已不再使用,不管它了,也不需要设置
  3. th32ProcessID:              这个参数就很明显了,代表一个进程的 ID,即进程标识符 。
  4. GlblcntUsage:                这个参数代表这个模块在整个系统中加载的数目 , 也不用理会 。
  5. ProccntUsage:               这个参数代表和前面的参数差不多,只不过不再是整个系统,而是指当前进程的上下文中,也不理会 。
  6. modBaseAddr:              代表模块在进程上下文中的基地址 。
  7. modBaseSize:                代表该模块的大小 。
  8. hModule :                       该值代表模块句柄 。
  9. szModule :                      该参数代表模块名。
  10. szExePath:                       回该模块所在的路径。

在使用当中呢,其实上面的这个结构体只需要填充第一个字段 dwSize 即可,

其他字段都是通过调用函数 Module32First 或者是 Module32Next 来填充的。

然后我们就来看属于 Module Walking 这一块的 API 了。

BOOL WINAPI Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
 
BOOL WINAPI Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

Module Walking 使用

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
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 = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", 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     = 0x%08X", me32.th32ProcessID);
        printf("\n     ref count (g)  =     0x%04X", me32.GlblcntUsage);
        printf("\n     ref count (p)  =     0x%04X", me32.ProccntUsage);
        printf("\n     base address   = 0x%08X", (DWORD)me32.modBaseAddr);
        printf("\n     base size      = %d", me32.modBaseSize);
 
    } while(Module32Next(hModuleSnap, &me32));
 
    CloseHandle(hModuleSnap);
    return TRUE;
}

程序效果展示:

可以看到我们当前运行的这个 Test.exe 的进程引用了很多个模块,
有可执行文件 Test.exe ,ntdll.dll ,kernel32.dll ,
以及 kernelbase.dll , msvcrt.dll

image

image

其实上面的这些 DLL 都是必须得,在 Test.exe 中引用了 kernel32.dllmsvcrt.dll

而在 Kernel32.dll 中则引用了 ntdll.dllkernelbase.dll

从下面的截图中就可以看出这点。

image

image

总结

上面呢通过一个一个的 Demo 来介绍了 ToolHelp 的使用,ToolHelp 听上去貌似蛮那个的,

其实说白了也就是 12 个 API 在用来用去的,通过前面的代码,也可以看出来,

通过 ToolHelp 来获取信息还是蛮方便的,

上面的代码呢,我也是通过 MSDN 获得,然后通过自己的一些改写以及整理出来的,

个人觉得还是蛮有参考价值的,有兴趣的可以留个纪念 。                                 

版权所有,迎转载,但转载请注明:     转载自    Zachary.XiaoZhen - 梦想的天空

posted @ 2011-02-27 17:50 小宝马的爸爸 阅读(...) 评论(...) 编辑 收藏