Skip to content

Instantly share code, notes, and snippets.

@decagondev
Last active September 1, 2025 23:52
Show Gist options
  • Save decagondev/f71a853a78ff5a499974db3984ef1a49 to your computer and use it in GitHub Desktop.
Save decagondev/f71a853a78ff5a499974db3984ef1a49 to your computer and use it in GitHub Desktop.

Process Information Tool Guide

This guide details the steps of the Process Information Tool (ProcessInfoTool.cpp), a C++ application that displays detailed information about running processes on a Windows system, including Process ID, name, parent process, memory usage, loaded modules, and thread count. Each step includes code snippets, reasoning, Mermaid diagrams to illustrate the process flow, and references to Microsoft documentation.

Prerequisites

  • Operating System: Windows 11 (x64 or ARM64 with x64 emulation).
  • Development Environment: Visual Studio 2022 with C++ Desktop Development workload and Windows SDK.
  • Libraries: Psapi.lib for process module and memory functions.
  • Permissions: Run as Administrator for full process access.

Step 1: Include Necessary Headers

Purpose: Include Windows API and standard C++ headers to access process, thread, and module information.

Code Snippet:

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>

Reasoning:

  • <windows.h> provides core Windows API functions like OpenProcess.
  • <tlhelp32.h> enables process and thread enumeration via CreateToolhelp32Snapshot.
  • <psapi.h> provides EnumProcessModules and GetProcessMemoryInfo for module and memory details.
  • <stdio.h> supports console output with printf.

Diagram:

graph TD
    A[Start] --> B[Include windows.h]
    B --> C[Include tlhelp32.h]
    C --> D[Include psapi.h]
    D --> E[Include stdio.h]
    E --> F[Access Windows API functions]
Loading

Documentation:

Step 2: Open Process with Required Access

Purpose: Open a process by its ID to query its information.

Code Snippet:

HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
if (hProcess == NULL) {
    printf("Unable to open process %d\n", processID);
    return;
}

Reasoning:

  • OpenProcess requires specific access rights: PROCESS_QUERY_INFORMATION for memory and module info, and PROCESS_VM_READ for reading module names.
  • If the handle is NULL, the process might be inaccessible (e.g., system process or insufficient permissions).
  • Running as Administrator reduces access errors.

Diagram:

graph TD
    A[Start] --> B[Call OpenProcess]
    B -->|Specify PROCESS_QUERY_INFORMATION| C[Check Handle]
    B -->|Specify PROCESS_VM_READ| C
    C -->|Handle Valid| D[Proceed to Query Process]
    C -->|Handle NULL| E[Print Error and Return]
Loading

Documentation:

Step 3: Get Process Name

Purpose: Retrieve the executable name of the process.

Code Snippet:

WCHAR processName[MAX_PATH] = L"<unknown>";
if (GetModuleBaseNameW(hProcess, NULL, processName, MAX_PATH)) {
    printf("\nProcess: %ws (PID: %d)\n", processName, processID);
} else {
    printf("\nProcess: <unknown> (PID: %d)\n", processID);
}

Reasoning:

  • GetModuleBaseNameW retrieves the name of the main executable module for the process.
  • Uses Unicode (WCHAR) for compatibility with modern Windows systems.
  • Fallback to "unknown" if the call fails (e.g., for protected processes).

Diagram:

graph TD
    A[Start] --> B[Initialize processName]
    B --> C[Call GetModuleBaseNameW]
    C -->|Success| D[Print Process Name and PID]
    C -->|Failure| E[Print unknown and PID]
Loading

Documentation:

Step 4: Get Parent Process ID

Purpose: Identify the parent process of the given process.

Code Snippet:

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
DWORD parentPID = 0;
if (hSnapshot != INVALID_HANDLE_VALUE) {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(hSnapshot, &pe32)) {
        do {
            if (pe32.th32ProcessID == processID) {
                parentPID = pe32.th32ParentProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
}
printf("Parent PID: %d\n", parentPID);

Reasoning:

  • CreateToolhelp32Snapshot with TH32CS_SNAPPROCESS creates a snapshot of all running processes.
  • Process32First and Process32Next iterate through processes to find the matching processID and retrieve th32ParentProcessID.
  • The snapshot handle is closed to prevent resource leaks.

Diagram:

graph TD
    A[Start] --> B[Create Snapshot with TH32CS_SNAPPROCESS]
    B -->|Valid Handle| C[Initialize PROCESSENTRY32]
    B -->|Invalid Handle| D[Set parentPID to 0]
    C --> E[Call Process32First]
    E --> F[Loop with Process32Next]
    F -->|Match processID| G[Set parentPID]
    F -->|No Match| H[Continue Loop]
    G --> I[Close Snapshot]
    H -->|End of Processes| I
    I --> J[Print parentPID]
Loading

Documentation:

Step 5: Get Memory Usage

Purpose: Retrieve memory usage statistics for the process.

Code Snippet:

PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
    printf("Working Set Size: %zu KB\n", pmc.WorkingSetSize / 1024);
    printf("Peak Working Set Size: %zu KB\n", pmc.PeakWorkingSetSize / 1024);
    printf("Pagefile Usage: %zu KB\n", pmc.PagefileUsage / 1024);
}

Reasoning:

  • GetProcessMemoryInfo populates a PROCESS_MEMORY_COUNTERS structure with memory usage data.
  • Key metrics include WorkingSetSize (current memory in RAM), PeakWorkingSetSize (maximum RAM usage), and PagefileUsage (virtual memory usage).
  • Values are converted to KB for readability.

Diagram:

graph TD
    A[Start] --> B[Initialize PROCESS_MEMORY_COUNTERS]
    B --> C[Call GetProcessMemoryInfo]
    C -->|Success| D[Print Working Set Size]
    D --> E[Print Peak Working Set Size]
    E --> F[Print Pagefile Usage]
    C -->|Failure| G[Skip Memory Info]
Loading

Documentation:

Step 6: Get Thread Count

Purpose: Count the number of threads owned by the process.

Code Snippet:

hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
int threadCount = 0;
if (hSnapshot != INVALID_HANDLE_VALUE) {
    THREADENTRY32 te32;
    te32.dwSize = sizeof(THREADENTRY32);
    if (Thread32First(hSnapshot, &te32)) {
        do {
            if (te32.th32OwnerProcessID == processID) {
                threadCount++;
            }
        } while (Thread32Next(hSnapshot, &te32));
    }
    CloseHandle(hSnapshot);
}
printf("Thread Count: %d\n", threadCount);

Reasoning:

  • CreateToolhelp32Snapshot with TH32CS_SNAPTHREAD captures all threads in the system.
  • Thread32First and Thread32Next iterate through threads, incrementing threadCount when th32OwnerProcessID matches the target process.
  • The snapshot handle is closed to free resources.

Diagram:

graph TD
    A[Start] --> B[Create Snapshot with TH32CS_SNAPTHREAD]
    B -->|Valid Handle| C[Initialize THREADENTRY32]
    B -->|Invalid Handle| D[Set threadCount to 0]
    C --> E[Call Thread32First]
    E --> F[Loop with Thread32Next]
    F -->|Match th32OwnerProcessID| G[Increment threadCount]
    F -->|No Match| H[Continue Loop]
    G --> H
    H -->|End of Threads| I[Close Snapshot]
    I --> J[Print threadCount]
Loading

Documentation:

Step 7: Get Loaded Modules

Purpose: List all modules (DLLs and executable) loaded by the process.

Code Snippet:

HMODULE hMods[1024];
DWORD cbNeeded;
printf("Loaded Modules:\n");
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
    for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
        WCHAR modName[MAX_PATH];
        if (GetModuleFileNameExW(hProcess, hMods[i], modName, MAX_PATH)) {
            printf("  %ws\n", modName);
        }
    }
}

Reasoning:

  • EnumProcessModules retrieves handles to all modules loaded by the process.
  • GetModuleFileNameExW gets the full path of each module.
  • The loop iterates through module handles, printing each module's path.
  • cbNeeded indicates the number of modules retrieved.

Diagram:

graph TD
    A[Start] --> B[Initialize Module Array]
    B --> C[Call EnumProcessModules]
    C -->|Success| D[Loop through Modules]
    C -->|Failure| E[Skip Module Listing]
    D --> F[Call GetModuleFileNameExW]
    F -->|Success| G[Print Module Path]
    F -->|Failure| H[Skip Module]
    G --> I[Next Module]
    H --> I
    I -->|More Modules| F
    I -->|No More Modules| J[End]
Loading

Documentation:

Step 8: Iterate Through All Processes

Purpose: Enumerate all running processes and apply the above steps to each.

Code Snippet:

int main() {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("Failed to create process snapshot\n");
        return 1;
    }
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(hSnapshot, &pe32)) {
        do {
            PrintProcessInfo(pe32.th32ProcessID);
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
    return 0;
}

Reasoning:

  • CreateToolhelp32Snapshot with TH32CS_SNAPPROCESS captures all processes.
  • Process32First and Process32Next iterate through each process, passing its ID to PrintProcessInfo.
  • Returns exit code 0 on success, 1 on snapshot failure.
  • The snapshot handle is closed to prevent leaks.

Diagram:

graph TD
    A[Start] --> B[Create Snapshot with TH32CS_SNAPPROCESS]
    B -->|Valid Handle| C[Initialize PROCESSENTRY32]
    B -->|Invalid Handle| D[Print Error and Exit]
    C --> E[Call Process32First]
    E -->|Success| F[Call PrintProcessInfo]
    F --> G[Call Process32Next]
    G -->|More Processes| F
    G -->|No More Processes| H[Close Snapshot]
    H --> I[Return 0]
Loading

Documentation:

Step 9: Build and Run

Purpose: Compile and execute the tool on a Windows 11 x64 system.

Steps:

  1. Install Visual Studio 2022 with the C++ Desktop Development workload and Windows SDK.
  2. Create a new Win32 Console Application project in Visual Studio.
  3. Add the ProcessInfoTool.cpp code to the main source file.
  4. Configure the project:
    • In Project Properties > General, set Platform Target to x64.
    • In Project Properties > Linker > Input, add Psapi.lib to Additional Dependencies.
  5. Build the solution by selecting Build > Build Solution or pressing Ctrl+Shift+B (Release) or F5 (Debug).
  6. Run the generated .exe as Administrator to ensure access to all process information.

Reasoning:

  • Visual Studio 2022 is the recommended IDE for building C++ applications on Windows 11 x64, providing native x64 compilation.
  • Psapi.lib is required for linking EnumProcessModules and GetProcessMemoryInfo functions.
  • Running as Administrator minimizes access-denied errors for system processes.
  • The x64 target ensures compatibility with the Windows 11 x64 environment, avoiding emulation overhead.

Diagram:

graph TD
    A[Start] --> B[Install Visual Studio 2022]
    B --> C[Create Win32 Console Project]
    C --> D[Add ProcessInfoTool.cpp]
    D --> E[Set Platform to x64]
    E --> F[Add Psapi.lib to Linker]
    F --> G[Build Solution]
    G --> H[Run as Administrator]
    H --> I[Display Process Info]
Loading

Documentation:

Additional Notes

  • Error Handling: The tool checks for invalid handles and failed API calls, printing errors where appropriate.
  • Permissions: Some system processes (e.g., PID 0 or 4) may be inaccessible without elevated privileges.
  • Performance: The tool processes all running processes, which may be resource-intensive on systems with many processes.
  • Compatibility: The code uses standard Windows APIs, fully compatible with Windows 11 x64.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment