sleeping.ninja
sleeping.ninja

Reputation: 607

Process Id's and process names

I'm creating a windows program that basically scans the system to see if a particular process is running or not. I have the process name (AcroRd32.exe) and nothing else.

From what I've read the easiest way to create a snapshot of all processes using CreateToolhelp32Snapshot and then iterate through each process looking for the process name.

My application is highly performance centric. So is there a better more efficient way to do this. The application collects a snapshot every few seconds. Iterating through 100's of processes in the snapshot doesn't seem efficient. Is there a direct API that can find the Process through its process name (and retrieve process handle or id through the name)?

I've searched extensively without much luck. Has anyone tried this before?

Upvotes: 1

Views: 5786

Answers (2)

subwar
subwar

Reputation: 279

The fastest way to scan for processes is via NTDLL's NtQuerySystemInformation call. It provides you with a list of names and process IDs of all processes on the system with a single call (or more in rare cases, i.e. large # of processes). You can combine NtQuerySystemInformation and use a hash to do string comparisons instead of comparing each byte.

// headers @ http://pastebin.com/HWzJYpbv


NtQuerySystemInformation = (_RT_NAPI_QUERYSYSINFO)GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQuerySystemInformation");

    // Get process information buffer
    do {
        // Allocate buffer for process info
        pBuffer = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbBuffer); 
        if (pBuffer == NULL) {
            // Cannot allocate enough memory for buffer (CRITICAL ERROR)
            return 1;
        }

        // Obtain system process snapshot
        Status = NtQuerySystemInformation(5, pBuffer, cbBuffer, NULL);

        // Allocate bigger buffer for moar data
        if (Status == STATUS_INFO_LENGTH_MISMATCH) {
            HeapFree(hHeap, 0, pBuffer);
            cbBuffer *= 2; // Increase the size of the buffer :-)
        } else if ((Status) != 0x00) {
            // Can't query process information (probably rootkit or anti-virus)
            HeapFree(hHeap, 0, pBuffer);
            return 1;
        }
    } while (Status == STATUS_INFO_LENGTH_MISMATCH);

    // Get pointer to first system process info structure
    pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;

    // Loop over each process
    for (;;) {
        // Get process name
        pszProcessName = pInfo->ImageName.Buffer;

        // ... do work. For a fast string compare, calculate a 32-bit hash of the string, then compare to a static hash.
        if(CRC32(pszProcessName) == 0xDEADBEEF /* <- hash of adobe reader process name goez here */) {
            // Found process
        }

        // Load next entry
        if (pInfo->NextEntryOffset == 0)
            break;
        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo)+ pInfo->NextEntryOffset);
    }

Tested on Windows 2000 - Windows 7 English editions, x64/x86 (except Win XP x64) Note: It will return all processes to 32-bit WOW64 processes on 64-bit systems.

Upvotes: 4

Eric Z
Eric Z

Reputation: 14505

No.

Each process has a unique ID but not unique name. There could be multiple processes with the same name. So it is impossible to get the process handle out of its name directly without iterating over all processes.

Internally all prcesses are linked together somehow, e.g., in a linked list. Even if there was a function GetProcessByName() provided, it would internally traverse the list to find those processes with that name on behalf of you as well. So that won't make a big difference in performance.

Aside

Give a shot to EnumProcesses() which has less overhead and is simpler. Check here.

BOOL WINAPI EnumProcesses(
  __out  DWORD *pProcessIds,
  __in   DWORD cb,
  __out  DWORD *pBytesReturned
);

MSDN has an example for this.

Upvotes: 3

Related Questions