Home > Articles > Operating Systems, Server > Microsoft Servers

  • Print
  • + Share This

Enumerating Processes and Threads

In Listing 4, the thread and process structures are shown. Note the different process structure definitions for Windows NT 4.0 and 2000—called SYSTEM_PROCESS_NT4 and SYSTEM_PROCESS_NT5, respectively. The SYSTEM_PROCESS_INFORMATION union at the end of Listing 4 is defined for convenience, allowing you to use a single data type for both operating systems. The Process item comprises all basic process structure members that are not version-specific, whereas Process_NT4 and Process_NT5 include all members that are available on Windows NT 4.0 and Windows 2000, respectively. If you are not interested in threads and Windows 2000 I/O counters, you can forget about the OS version and work with the common process structure only. Otherwise, you should use the Win32 API function GetVersionEx() to determine the major OS version of the system your application is currently running on.

Listing 4 Thread and Process Structures

typedef struct _SYSTEM_THREAD
    {
/*000*/ FILETIME   ftKernelTime;   // 100 nsec units
/*008*/ FILETIME   ftUserTime;    // 100 nsec units
/*010*/ FILETIME   ftCreateTime;   // relative to 01-01-1601
/*018*/ DWORD    dWaitTime;
/*01C*/ PVOID    pStartAddress;
/*020*/ CLIENT_ID  Cid;        // process/thread ids
/*028*/ DWORD    dPriority;
/*02C*/ DWORD    dBasePriority;
/*030*/ DWORD    dContextSwitches;
/*034*/ DWORD    dThreadState;   // 2=running, 5=waiting
/*038*/ KWAIT_REASON WaitReason;
/*03C*/ DWORD    dReserved01;
/*040*/ }
    SYSTEM_THREAD, *PSYSTEM_THREAD;
#define SYSTEM_THREAD_ sizeof (SYSTEM_THREAD)
// -----------------------------------------------------------------
typedef struct _SYSTEM_PROCESS     // common members
    {
/*000*/ DWORD     dNext;      // relative offset
/*004*/ DWORD     dThreadCount;
/*008*/ DWORD     dReserved01;
/*00C*/ DWORD     dReserved02;
/*010*/ DWORD     dReserved03;
/*014*/ DWORD     dReserved04;
/*018*/ DWORD     dReserved05;
/*01C*/ DWORD     dReserved06;
/*020*/ FILETIME    ftCreateTime;  // relative to 01-01-1601
/*028*/ FILETIME    ftUserTime;   // 100 nsec units
/*030*/ FILETIME    ftKernelTime;  // 100 nsec units
/*038*/ UNICODE_STRING usName;
/*040*/ KPRIORITY   BasePriority;
/*044*/ DWORD     dUniqueProcessId;
/*048*/ DWORD     dInheritedFromUniqueProcessId;
/*04C*/ DWORD     dHandleCount;
/*050*/ DWORD     dReserved07;
/*054*/ DWORD     dReserved08;
/*058*/ VM_COUNTERS  VmCounters;   // see ntddk.h
/*084*/ DWORD     dCommitCharge;  // bytes
/*088*/ }
    SYSTEM_PROCESS, *PSYSTEM_PROCESS;
#define SYSTEM_PROCESS_ sizeof (SYSTEM_PROCESS)
// -----------------------------------------------------------------
typedef struct _SYSTEM_PROCESS_NT4   // Windows NT 4.0
    {
/*000*/ SYSTEM_PROCESS Process;     // common members
/*088*/ SYSTEM_THREAD aThreads [];   // thread array
/*088*/ }
    SYSTEM_PROCESS_NT4, *PSYSTEM_PROCESS_NT4;
#define SYSTEM_PROCESS_NT4_ sizeof (SYSTEM_PROCESS_NT4)
// -----------------------------------------------------------------
typedef struct _SYSTEM_PROCESS_NT5   // Windows 2000
    {
/*000*/ SYSTEM_PROCESS Process;     // common members
/*088*/ IO_COUNTERS  IoCounters;   // see ntddk.h
/*0B8*/ SYSTEM_THREAD aThreads [];   // thread array
/*0B8*/ }
    SYSTEM_PROCESS_NT5, *PSYSTEM_PROCESS_NT5;
#define SYSTEM_PROCESS_NT5_ sizeof (SYSTEM_PROCESS_NT5)
// -----------------------------------------------------------------
typedef union _SYSTEM_PROCESS_INFORMATION
    {
/*000*/ SYSTEM_PROCESS   Process;
/*000*/ SYSTEM_PROCESS_NT4 Process_NT4;
/*000*/ SYSTEM_PROCESS_NT5 Process_NT5;
/*0B8*/ }
    SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

In Listing 4, some data types appear that are not defined in the Platform SDK header files—for example, CLIENT_ID, KPRIORITY, KWAIT_REASON, UNICODE_STRING, and VM_COUNTERS. These types are defined in the DDK header files, and must be added manually to your application code. But don't worry—I have already done that for you! (Click here to download all the files and source code discussed in this article).

So, the last piece missing in this puzzle is the trial-and-error loop mentioned earlier, which will determine an output buffer size that approximately matches the required number of bytes. Listing 5 shows the details: The ProcessInformation() function defined there calls NtQuerySystemInformation() in a loop until it succeeds, or returns an error code other than STATUS_INFO_LENGTH_MISMATCH. On each try, the buffer size is doubled, starting with an initial size of 4096 bytes.

Listing 5 Retrieving the Process/Thread List

PSYSTEM_PROCESS_INFORMATION ProcessInformation (PDWORD  pdData,
                        PNTSTATUS pns)
  {
  DWORD            dSize;
  DWORD            dData = 0;
  NTSTATUS          ns  = STATUS_INVALID_PARAMETER;
  PSYSTEM_PROCESS_INFORMATION pspi = NULL;
  for (dSize = 0x1000; (pspi == NULL) && dSize; dSize <<=
1)
    {
    if ((pspi = LocalAlloc (LMEM_FIXED, dSize)) == NULL)
      {
      ns = STATUS_NO_MEMORY;
      break;
      }
    ns = NtQuerySystemInformation (SystemProcessInformation,
                    pspi, dSize, &dData);
    if (ns != STATUS_SUCCESS)
      {
      LocalFree (pspi);
      pspi = NULL;
      dData = 0;
      if (ns != STATUS_INFO_LENGTH_MISMATCH) break;
      }
    }
  if (pdData != NULL) *pdData = dData;
  if (pns  != NULL) *pns  = ns;
  return pspi;
  }
  • + Share This
  • 🔖 Save To Your Account

Related Resources

There are currently no related titles. Please check back later.