Добрый день. Надо отследить появление новых процессов. Есть ли в Windows слот, на который поступает сигнал при появлении нового процесса? Иначе как это можно реализовать? В psapi подобного не нашёл. Есть предположение через определённые интервалы времени сверять список PID, но надеюсь найти более простое решение.
Ответ
Можно сделать трейсинг через системную функцию ZwQuerySystemInformation. Внимание! Microsoft не рекомендует, начиная с Windows 8, её использовать, предлагая замену, но, как проверено только что на Windows 10, она работает.
Пример реализации и использования привожу на Delphi, в виде консольного приложения. Поскольку речь касается функций WinAPI, переписать на C/C++ - легко.
program ListOfProcess;
{$APPTYPE CONSOLE}
uses
SysUtils,
windows,
System.Generics.Collections;
// поскольку подход писался очень давно (во времена Windows 2000),
// типы и константы определены, поскольку в то время их ещё не было в
// системных модулях Delphi. Хотя и сейчас, возможно, некоторых нет.
type
NTStatus = cardinal;
PVOID = pointer;
USHORT = WORD;
UCHAR = byte;
PWSTR = PWideChar;
const
STATUS_SUCCESS = NTStatus($00000000);
STATUS_ACCESS_DENIED = NTStatus($C0000022);
STATUS_INFO_LENGTH_MISMATCH = NTStatus($C0000004);
const SystemProcessesAndThreadsInformation = 5;
type
PClientID = ^TClientID;
TClientID = packed record
UniqueProcess:cardinal;
UniqueThread:cardinal;
end;
PUnicodeString = ^TUnicodeString;
TUnicodeString = packed record
Length: Word;
MaximumLength: Word;
Buffer: PWideChar;
end;
PVM_COUNTERS = ^VM_COUNTERS;
VM_COUNTERS = packed record
PeakVirtualSize,
VirtualSize,
PageFaultCount,
PeakWorkingSetSize,
WorkingSetSize,
QuotaPeakPagedPoolUsage,
QuotaPagedPoolUsage,
QuotaPeakNonPagedPoolUsage,
QuotaNonPagedPoolUsage,
PagefileUsage,
PeakPagefileUsage: dword;
end;
PIO_COUNTERS = ^IO_COUNTERS;
IO_COUNTERS = packed record
ReadOperationCount,
WriteOperationCount,
OtherOperationCount,
ReadTransferCount,
WriteTransferCount,
OtherTransferCount: LARGE_INTEGER;
end;
PSYSTEM_THREADS = ^SYSTEM_THREADS;
SYSTEM_THREADS = packed record
KernelTime,
UserTime,
CreateTime: LARGE_INTEGER;
WaitTime: dword;
StartAddress: pointer;
ClientId: TClientId;
Priority,
BasePriority,
ContextSwitchCount: dword;
State: dword;
WaitReason: dword;
end;
PSYSTEM_PROCESSES = ^SYSTEM_PROCESSES;
SYSTEM_PROCESSES = packed record
NextEntryDelta,
ThreadCount: dword;
Reserved1 : array [0..5] of dword;
CreateTime,
UserTime,
KernelTime: LARGE_INTEGER;
ProcessName: TUnicodeString;
BasePriority: dword;
ProcessId,
InheritedFromProcessId,
HandleCount: dword;
Reserved2: array [0..1] of dword;
VmCounters: VM_COUNTERS;
IoCounters: IO_COUNTERS;
Threads: array [0..0] of SYSTEM_THREADS;
end;
// основная функция, которую мы будем использовать
Function ZwQuerySystemInformation(ASystemInformationClass: dword;
ASystemInformation: Pointer;
ASystemInformationLength: dword;
AReturnLength:PCardinal): NTStatus;
stdcall;external 'ntdll.dll';
// Функция получения буфера с системной информацией
Function GetInfoTable(ATableType:dword):Pointer;
var
mSize: dword;
mPtr: pointer;
St: NTStatus;
begin
Result := nil;
mSize := $4000; //начальный размер буфера
repeat // подгонка размера буфера
mPtr := VirtualAlloc(nil, mSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
if mPtr = nil then Exit;
St := ZwQuerySystemInformation(ATableType, mPtr, mSize, nil);
if St = STATUS_INFO_LENGTH_MISMATCH then
begin
VirtualFree(mPtr, 0, MEM_RELEASE);
mSize := mSize * 2;
end;
until St <> STATUS_INFO_LENGTH_MISMATCH;
if St = STATUS_SUCCESS
then Result := mPtr
else VirtualFree(mPtr, 0, MEM_RELEASE);
end;
// приведу пример использовании фунции:
var info, info2: PSystem_Processes;
i, j, k: integer;
t, t1: LARGE_INTEGER;
process_id:TDictionary
// Список процессов на момент старта программы
info := GetInfoTable(SystemProcessesAndThreadsInformation);
info2 := info;
while (info2^.NextEntryDelta <> 0) do
begin
if not process_id.ContainsKey(info2^.ProcessId) then
process_id.Add(info2^.ProcessId, info2^);
info2 := Pointer(dword(info2)+info2^.NextEntryDelta);
end;
VirtualFree(info, 0, MEM_RELEASE);
while true do // нужно вставить вариант выхода из цикла. Разумеется, Ctrl+C всегда сработает :)
begin
Sleep(2000); // будем проверять наличие новых процессов каждые 2 секунды
info := GetInfoTable(SystemProcessesAndThreadsInformation);
info2 := info;
while (info2^.NextEntryDelta <> 0) do
begin
if not process_id.ContainsKey(info2^.ProcessId)
then
begin
writeln(info2^.ProcessId, ' - появился');
process_id.Add(info2^.ProcessId,info2^); // если будем постоянно добавлять, словарь разрастётся,
// надо бы подумать и об удалении элементов, оставляю ТС :)
end;
info2 := Pointer(dword(info2)+info2^.NextEntryDelta);
end;
VirtualFree(info, 0, MEM_RELEASE);
end;
process_id.Free;
end.
Комментариев нет:
Отправить комментарий