Process Enumeration in Windows 2000






4.60/5 (9 votes)
Jun 11, 2002
4 min read

125393

1097
Explains how to use the ToolHelp API to enumerate processes under Windows 2000/XP
Introduction
The advent of NT 5.0 has not only given a new face and improved technology to Windows NT 4.0 users, but has significant improvements for software developers as well, like introduction of COM+, expansion of the Win32 API set, just to name a few.
Under the expansion of the Win32 API, NT 5.0 now supports a new (for Windows NT) set of APIs which help enumerate, amongst other things, the processes and threads in the system. This API set was already present in Windows 95/98, but wasn't there in Windows NT 4.0, where this enumeration required interaction with the system registry.
This article intends to illustrate the enumeration of the processes and threads under a NT 5.0 system using this new set of APIs, which constitute the 32bit TOOLHELP API set (the prototypes are declared in TLHELP32.H). With introduction behind us, lets start enumerating.The Snapshot
Whenever we use the TOOLHELP API to enumerate the system processes and threads, we have to create, what is called a ToolHelp Snapshot. This snapshot gets the details regarding the processes and threads, amongst other things, at a given point of time (which is the time the snapshot is created). To create the snapshot, we use theCreateToolhelp32Snapshot
API whose syntax is:HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
dwFlags
tells the API whose snapshot do we wish to take. Passing the constant TH32CS_SNAPPROCESS
takes a snapshot of all the active processes, while TH32CS_SNAPTHREAD
does the same for the threads in the system. th32ProcessID
is the ID of the process in whose context we intend to take the snapshot. We usually specify 0(zero) when taking snapshots of processes and threads, which aren't specific to a particular process. On the other hand, if we intend to enumerate a process specific entity, say the heap list, then we specify the ID of the process whose heap list we wish to enumerate.
If the snapshot is successfully created, the API returns a HANDLE
to the snapshot. Otherwise, -1 is returned.
Onto Enumeration
Once we have the handle to the snapshot of the processes or threads or both (by ORingTH32CS_SNAPPROCESS
and TH32CS_SNAPTHREAD
and creating a snapshot), we proceed to enumerate the processes or threads.
TOOLHELP API exports two functions for enumerating Processes: Process32First
and Process32Next
. Process32First
has the following prototype:
BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
and Process32Next
has an almost identical one:
BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
Both the APIs take the handle to a Process Snapshot and a pointer to a structure which shall contain the information regarding the enumerated process. This structure has the following structure:
typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; DWORD th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; DWORD dwFlags; char szExeFile[MAX_PATH]; } PROCESSENTRY32; typedef PROCESSENTRY32 * PPROCESSENTRY32; typedef PROCESSENTRY32 * LPPROCESSENTRY32;
As you can see, LPPROCESSENTRY32
is basically a pointer to PROCESSENTRY32
. The various fields of the structure are briefly documented below :
dwSize:
contains the size of the structure and must be initialized before passing the pointer to the structure to the APIcntUsage:
tells how many processes are using this process. One process may "use" another process, say for example, by opening that process. This is returned after enumeration.th32ProcessID:
contains the ID of the process. This is returned after enumeration.th32DefaultHeapID:
contains the ID of the default heap of the process. This is returned after enumeration.th32ModuleID:
contains the ID of the module associated with the process. This is returned after enumeration.cntThreads:
contains the number of threads belonging to this process. This is returned after enumeration.th32ParentProcessID:
contains the process ID of the process which is parent to this process. This is returned after enumeration.pcPriClassBase:
contains the base priority class of the process. This is returned after enumeration.dwFlags:
officially, its documented as reserved!!!szExeFile:
returns the path to the executable from which this process was created.
To start enumeration, we first call Process32First
with the snapshot of the processes and a pointer to PROCESSENTRY32
structure (after filling its dwSize
field). The API returns TRUE
upon a successful enumeration and FALSE
otherwise. Assuming a TRUE
response, the PROCESSENTRY32
structure is filled with all the details regarding the process enumerated.
PROCESSENTRY32
structure (after filling its dwSize field). Assuming a TRUE
response, the PROCESSENTRY32
structure is filled with all the details regarding the process enumerated. Continue to do this repeatedly until the API returns FALSE
.
Once the enumeration is complete, DON'T FORGET TO CLOSE THE HANDLE TO THE SNAPSHOT. Below I present the pseudo code to enumerate the process list under NT 5.0. Here it is:
. #include <tlhelp32.h> . . . HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_PROCESS,0); if (hSnapshot==-1) { // error: unable to create snapshot } PROCESSENTRY32 pe; // fill up its size pe.dwSize=sizeof(PROCESSENTRY32); BOOL retval=Process32First(hSnapshot,&pe); while(retval) { printf("Process ID : %08X\n",pe.th32ProcessID); pe.dwSize=sizeof(PROCESSENTRY32); retval=Process32Next(hSnapshot,&pe); } // close snapshot handle CloseHandle(hSnapshot); // viola.. we are done
Conclusion
So, that's it! The same code now can be used to enumerate processes under Windows 95/98 without any change!! Isn't that a relief... a more portable code. And with quite reduced complexity as compared to how it was done under NT 4.0!