Listing processes on Windows in C

Introduction

I was writing something recently which required obtaining a list of running processes on Windows. The problem was that it had to run on systems as early as Windows NT right up to Windows 10 and there’s no single API you can use for this.

I also had to pick a compiler that would support Windows NT since Microsoft generally avoid supporting legacy operating systems with their compilers after a certain point.

API options

So what API are available to use?

  • Performance Data
  • Available since Windows NT but undocumented. Mark Russinovich’s pslist uses this so it can also work remotely provided the Remote Registry Service is enabled.

  • Win32_Process
  • Seems to be available since Windows NT. Tried checking with VBScript but cscript.exe wasn’t installed. This would be ideal if you needed to list processes on a remote system but is also much harder to use when programming with C++ instead of a scripting language such as VBScript or Powershell.

  • Process32First
  • You can certainly use this for 32 and 64-bit now since Windows XP but it’s not available on Windows NT.

  • EnumProcesses
  • Apparently the PSAPI.DLL file where this API resides can be installed from CD-ROM but it’s not installed by default which means we can’t depend on it.

  • NtQuerySystemInformation
  • Available since Windows NT so we can use it for that but doesn’t work for 64-bit systems.

    The solution is to use NtQuerySystemInformation if executing on Wow64 or legacy systems and to use Process32First if we’re building for 64-bit systems.

    Process Entry Structure

    Rather than work with 2 different structures, I only copy the module name and process id to a new structure which is then parsed by the callee. The module names are in unicode format.

    typedef struct _PROCENTRY_T {
      DWORD id;
      WCHAR name[MAX_PATH];
    } PROCENTRY, *PPROCENTRY;
    

    Legacy Mode

    So here’s a little function in C called GetProcessList which uses those 2 API to retrieve a list of running processes.

    PPROCENTRY GetProcessList(VOID)
    {
      pNtQuerySystemInformation   NtQuerySystemInformation;
      pRtlCompareUnicodeString    RtlCompareUnicodeString;
      ULONG                       len=0, total=0, pe_size=0;
      NTSTATUS                    status;
      LPVOID                      list=NULL;
      PSYSTEM_PROCESS_INFORMATION p;
      PPROCENTRY                  pe;
      DWORD                       i;
      
      NtQuerySystemInformation = 
          (pNtQuerySystemInformation)GetProcAddress(
          GetModuleHandle("ntdll"), "NtQuerySystemInformation");
          
      if (!NtQuerySystemInformation) {
        // we couldn't resolve API address
        return NULL;
      }
      
      list = xmalloc(2048);
      
      do {
        len += 2048;
        list = xrealloc (list, len);
        
        if (list==NULL) {
          // we couldn't reallocate memory
          break;
        }
        status = NtQuerySystemInformation(SystemProcessInformation, list, len, &total);
      } while (status == STATUS_INFO_LEN_MISMATCH);
      
      if (status < 0) {
        // we were unable to obtain list of process
        xfree(list);
        return NULL;
      }
      
      p       = (PSYSTEM_PROCESS_INFORMATION)list;
      pe_size = sizeof(PROCENTRY);
      pe      = xmalloc(pe_size);
      
      for (i=0;;) 
      {
        if (p->ProcessName.Buffer != 0)
        {
          // copy process id and module name
          pe[i].id = p->ProcessId; 
          lstrcpy(pe[i].name, p->ProcessName.Buffer);
          
          pe_size += sizeof(PROCENTRY);
          pe = xrealloc(pe, pe_size);
          i++;
          if (pe==NULL) {
            break;
          }        
        }
        // no more entries? break
        if (p->NextEntryDelta==0) break;
        
        // advance to next entry
        p = (PSYSTEM_PROCESS_INFORMATION)(((char *)p) + p->NextEntryDelta);
      }
      
      xfree(list);
      return pe;
    }
    

    Long Mode

    PPROCENTRY GetProcessList(VOID)
    {
      HANDLE         hSnap;
      PROCESSENTRY32 pe32;
      PPROCENTRY     pe=NULL;
      DWORD          i=0, pe_size;
      
      hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
      if (hSnap != INVALID_HANDLE_VALUE)
      {
        pe32.dwSize = sizeof(PROCESSENTRY32);
    
        if (Process32First(hSnap, &pe32))
        {
          i       = 0;
          pe_size = sizeof(PROCENTRY);
          pe      = xmalloc(pe_size);
      
          do {
            if (pe32.th32ProcessID==0) continue;
            
            pe[i].id = pe32.th32ProcessID; 
            lstrcpy(pe[i].name, pe32.szExeFile);
            
            pe_size += sizeof(PROCENTRY);
            pe = xrealloc(pe, pe_size);
            i++;
            if (pe==NULL) {
              break;
            }            
          } while (Process32Next(hSnap, &pe32));
        }
        CloseHandle(hSnap);
      }
      return pe;
    }
    

    Demonstration

    The following is small example of using the above functions.

    int main(void)
    {
      PPROCENTRY pe;  
      PPROCENTRY list = GetProcessList();
      
      if (list==NULL) {
        printf ("\nUnable to retrieve list of process");
        return 0;
      }
      printf ("\nList of processes");
      printf ("\n=================");
      for (pe=list; pe->id; pe++) {
        printf ("\n%-30ws - %i", pe->name, pe->id);
      }
      xfree(list);  
      return 0;
    }
    

    See pslist.c here

    To compile for just listing processes, use CL /DTEST pslist.c

    Advertisements
    This entry was posted in programming, windows and tagged , , , , . Bookmark the permalink.

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s