El problema debería ser mas sencillo de lo que es pero Microsoft gusta de no facilitar las cosas con la intuición.
Para medir el consumo de la CPU debemos medir el tiempo que dedica a una tarea no activa, calcular el porcentaje y restarlo de 100, otra forma es usar la PDH_API para poner en marcha un contador que nos dará una medida puntual del consumo de la CPU. Esta medida es puntual, por lo que si queremos tener una medición por segundo, deberemos hacer varias medidas y hacer una media en ese intervalo. El siguiente código nos realiza una medida puntual
var
hQuery: PDH_HQUERY;
cpuTotal: PDH_HCOUNTER;
//Para consumo de CPU
procedure InitSampleCPU;
begin
PdhOpenQuery(nil, 0, hQuery);
PdhAddEnglishCounterA(hQuery, '\Processor(_Total)\% Processor Time', 0, cpuTotal);
end;
function GetSampleCPUValue: double;
var
counterVal: PDH_FMT_COUNTERVALUE;
begin
Status:= PdhCollectQueryData(hQuery);
Status:= PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, nil, @counterVal);
Result:= counterVal.doubleValue;
end;
Para medir el consumo de un proceso, es mejor medir el consumo gastado en modo Kernel, sumarlo al consumo de modo usuario y dividirlo por el tiempo de CPU realmente transcurrido entre dos mediciones, entre el número de procesadores y multiplicarlo por 100. De esa forma obtenemos el porcentaje de tiempo de CPU usado por un proceso en un intervalo de tiempo dado (entre dos medidas)
He definido la estructura TIME_PROC_VALUE para guardar y refrescar los tiempos obtenidos de la medida anterior, de esta forma conseguimos un valor medio entre las dos medidas. Si queremos monitorizar más de un proceso, debemos declarar una variable del tipo TIME_PROC_VALUE para cada uno de ellos, inicializarla y usarla como parámetro para realizar la medida con la función implementada más abajo: GetProcessValue
type
TIME_PROC_VALUE = record
PID: DWORD;
lastCPU: ULARGE_INTEGER;
lastUTime: ULARGE_INTEGER;
lastKTime: ULARGE_INTEGER;
end;
PTIME_PROC_VALUE = ^TIME_PROC_VALUE;
var
numProcessors: integer;
TV: TIME_PROC_VALUE;
procedure InitFromPID(PID: DWORD; var TimeValue: TIME_PROC_VALUE);
var
sysInfo: SYSTEM_INFO;
time: FILETIME;
hProcess: THANDLE;
begin
GetSystemInfo(sysInfo);
numProcessors:= sysInfo.dwNumberOfProcessors;
GetSystemTimeAsFileTime(FILETIME(TimeValue.lastCPU));
hProcess:= OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, PID);
GetProcessTimes(hProcess, time, time, FILETIME(TimeValue.lastKTime), FILETIME(TimeValue.lastUTime));
CloseHandle(hProcess);
TimeValue.PID:= PID;
end;
function GetProcessValue(var TimeValue: TIME_PROC_VALUE): double;
var
time: FILETIME;
now, kernel, user: ULARGE_INTEGER;
hProcess: THANDLE;
begin
Result:= 0;
hProcess:= OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, TimeValue.PID);
if hProcess <> 0 then
begin
GetSystemTimeAsFileTime(FILETIME(now));
GetProcessTimes(hProcess, time, time, FILETIME(kernel), FILETIME(user));
Result:= (kernel.QuadPart - TimeValue.lastKTime.QuadPart) + (user.QuadPart - TimeValue.lastUTime.QuadPart);
Result:= 100*((Result / (now.QuadPart - TimeValue.lastCPU.QuadPart)) / numProcessors);
TimeValue.lastCPU:= now;
TimeValue.lastKTime:= kernel;
TimeValue.lastUTime:= user;
CloseHandle(hProcess);
end;
end;
Subo un pequeño programita como ejemplo. En él no me he molestado en sacar el consumo de la CPU como media en un intervalo, obtengo sólo consumos puntuales, Una idea que dejo en el aire es tomar 10 medidas durante un segundo y realizar la media que se mostrará cada segundo
El código está probado en delphi 7 pero no debe haber dificultades para portarlo a Berlin o Lazarus, observar que uso la versión AnsiChar de la API PdhAddEnglishCounter (PdhAddEnglishCounterA) el que lo prefiera puede usar la versión UNICODE (PdhAddEnglishCounterW)
Saludos.