Enumerate Process via NtQuerySystemInformation - Delphi

DarkCoderSc
Jean-Pierre LESUEUR
uses
System.SysUtils, Winapi.Windows, Generics.Collections;
// ...
const
SYSTEM_PROCESS_INFORMATION_CLASS = 5;
// ...
type
UNICODE_STRING = record
Length : USHORT;
MaximumLength : USHORT;
Buffer : PWideChar;
end;
TUnicodeString = UNICODE_STRING;
PUnicodeString = ^TUnicodeString;
SYSTEM_PROCESS_INFORMATION = record
NextEntryOffset : ULONG;
NumberOfThreads : ULONG;
WorkingSetPrivateSize : LARGE_INTEGER;
HardFaultCount : ULONG;
NumberOfThreadsHighWaterMark : ULONG;
CycleTime : ULONGLONG;
CreateTime : _FILETIME;
UserTimer : LARGE_INTEGER;
KernelTime : LARGE_INTEGER;
ModuleName : TUnicodeString;
BasePriority : LONG;
ProcessID : NativeUInt;
InheritedFromProcessId : NativeUInt;
HandleCount : ULONG;
SessionId : ULONG;
UniqueProcessKey : ULONG_PTR;
PeakVirtualSize : ULONG_PTR;
VirtualSize : ULONG_PTR;
PageFaultCount : ULONG;
PeakWorkingSetSize : ULONG_PTR;
WorkingSetSize : ULONG_PTR;
QuotePeakPagedPoolUsage : ULONG_PTR;
QuotaPagedPoolUsage : ULONG_PTR;
QuotaPeakNonPagedPoolUsage : ULONG_PTR;
QuotaNonPagedPoolUsage : ULONG_PTR;
PagefileUsage : ULONG_PTR;
PeakPagefileUsage : ULONG_PTR;
PrivatePageCount : ULONG_PTR;
ReadOperationCount : LARGE_INTEGER;
WriteOperationCount : LARGE_INTEGER;
OtherOperationCount : LARGE_INTEGER;
ReadTransferCount : LARGE_INTEGER;
WriteTransferCount : LARGE_INTEGER;
OtherTransferCount : LARGE_INTEGER
end;
TSystemProcessInformation = SYSTEM_PROCESS_INFORMATION;
PSystemProcessInformation = ^TSystemProcessInformation;
// ...
function NtQuerySystemInformation(
SystemInformationClass : DWORD;
SystemInformation : Pointer;
SystemInformationLength : DWORD;
var ReturnLength : DWORD
) : Cardinal; stdcall; external 'NTDLL.DLL';
// ...
function EnumerateProcess_NtQuerySystemInformation(var AList : TDictionary<Cardinal, String>) : Cardinal;
begin
result := 0;
if not Assigned(AList) then
AList := TDictionary<Cardinal, String>.Create()
else
AList.Clear();
///
var AReturnLength : DWORD;
var ARet := NtQuerySystemInformation(SYSTEM_PROCESS_INFORMATION_CLASS, nil, 0, AReturnLength);
if (ARet <> 0) and (ARet <> $C0000004) (* STATUS_INFO_LENGTH_MISMATCH *) then
raise EWindowsException.Create('NtQuerySystemInformation(1)');
var pFirstRow : PSystemProcessInformation;
GetMem(pFirstRow, AReturnLength);
try
ARet := NtQuerySystemInformation(SYSTEM_PROCESS_INFORMATION_CLASS, pFirstRow, AReturnLength, AReturnLength);
if ARet <> 0 then
raise EWindowsException.Create('NtQuerySystemInformation(2)');
var pNextRow := pFirstRow;
while True do begin
var AProcessName := '';
if pNextRow^.ModuleName.Length = 0 then
AProcessName := 'Unknown'
else
AProcessName := String(pNextRow^.ModuleName.Buffer);
AList.Add(pNextRow^.ProcessID, AProcessName);
if pNextRow^.NextEntryOffset = 0 then
break;
///
pNextRow := Pointer(NativeUInt(pNextRow) + pNextRow.NextEntryOffset);
end;
finally
FreeMem(pFirstRow, AReturnLength);
end;
///
result := AList.Count;
end;
// ...
var AList := TDictionary<Cardinal, String>.Create();
try
EnumerateProcess_NtQuerySystemInformation(AList);
for var AProcessId in AList.Keys do begin
var AImagePath : String;
if not AList.TryGetValue(AProcessId, AImagePath) then
continue;
WriteLn(Format('%s (%d)', [
AImagePath,
AProcessId
]));
end;
finally
FreeAndNil(AList);
end;
Creating and researching code snippets takes time and effort. You’re welcome to share them through your own platforms, but please don’t forget to credit the original author, here: Jean-Pierre LESUEUR.
Implemented By Technique
Featured Windows API
Created
April 24, 2025
Last Revised
April 24, 2025