Назад |
Скачай демонстрационный проект (26,1 кб)
Использовать информацию о производительности системы непосредственно из реестра, мягко выражаясь, муторно. Для облегчения получения этой инфы была разработана библиотека Performance Data Helper (pdh.dll). Она появилась еще в Windows NT 4.0 Для получения информации из реестра библиотека использует все ту-же функцию RegQueryValueEx, но позволяет получать уже сосчитанные и отформатированные данные счетчиков.
Рисунок 1 Архитектура получения информации о производительности системы (из MSDN)
В основе работы лежит запрос (Query). Можно назвать его еще сессией работы со счетчиками производительности. Программа отрывает запрос(сессию) – PdhOpenQuery. После этого можно добавлять в этот запрос счетчики (Counter) производительности – PdhAddCounter. Концепция этих счетчиков такая-же, как и при работе с реестром. Запрошенные данные собираются при вызове функции PdhCollectQueryData, а отформатированные значения счетчиков (типа загрузки процессора, используемой памяти и т.п.) возвращаются функцией PdhGetFormattedCounterValue (или PdhGetFormattedCounterArray – если надо получить массив значений). Когда данные больше не нужны запрос надо закрыть – PdhCloseQuery. Вот такой в общих чертах механизм работы с библиотекой, хотя возможностей она предоставляет гораздо больше. Интересующихся подробностями отправляю в MSDN: Platform SDK: Performance Monitoring.
Чтобы просто получить список процессов не надо даже открывать запрос. Нужна только одна функция PdhEnumObjectItems. Пример кода смотри в статье “HKEY_PERFORMANCE_DATA – виртуальный реестр”. А тут, для иллюстрации возможностей библиотеки, мы получить список процессов с из PID’ами и загрузкой процессора. Привожу код всего модуля, т.к. здесь использую разделы инициализации и завершения модуля.
unit CPU_Monitor;
interface
uses Windows, Classes, SysUtils;
// Основная функция: Получает объект-список и заполняет его данными о процессах
// Возвращает True при успешном выполнении
function FillProcessesList(var slProcesses: TStringList): Boolean;
implementation
const
// Пути к счетчикам. * - означает включение всех процессов
PATH_PID = '\Process(*)\ID Process';
PATH_USAGE = '\Process(*)\% Processor Time';
PDH_FMT_LONG = $00000100; // Флаги формата значения счетчика
PDH_FMT_LARGE = $00000400;
type
PDH_STATUS = LongInt; // Возвращаемое значение функций PDH
HQUERY = THandle; // Хэндл запроса
HCOUNTER = THandle; // Хэндл счетчика
PDH_FMT_COUNTERVALUE = record //Форматированное значение счетчика
CStatus: LongInt;
case Integer of
1: (longValue: LongInt);
2: (doubleValue: Double);
3: (largeValue: int64);
4: (AnsiStringValue: PChar);
5: (WideStringValue: PWideChar);
end;
PDH_FMT_COUNTERVALUE_ITEM_A = record //Элемент получаемого массива счетчиков
szName: PChar; // Тут будет имя процесса
FmtValue: PDH_FMT_COUNTERVALUE; // Значение счетчика
end;
PPDH_FMT_COUNTERVALUE_ITEM_A = ^PDH_FMT_COUNTERVALUE_ITEM_A;
// Линкуем функции pdh.dll статически
function PdhOpenQuery(szDataSource: LPCSTR; dwUserData: DWORD;
var hQuery: HQUERY): PDH_STATUS; stdcall;
external 'PDH.DLL' name 'PdhOpenQueryA';
function PdhAddCounter(hQuery: HQUERY; szFullPathBuffer: PChar;
dwUserData: DWORD; var phCounter: HCOUNTER): PDH_STATUS;
stdcall; external 'PDH.DLL' name 'PdhAddCounterA';
function PdhCollectQueryData(hQuery: HQUERY): PDH_STATUS; stdcall;
external 'PDH.DLL' name 'PdhCollectQueryData';
function PdhRemoveCounter(hCounter: HCOUNTER): PDH_STATUS; stdcall;
external 'PDH.DLL' name 'PdhRemoveCounter';
function PdhCloseQuery(hQuery: HQUERY): PDH_STATUS; stdcall;
external 'PDH.DLL' name 'PdhCloseQuery';
function PdhGetFormattedCounterArray(hCounter: HCOUNTER; dwFormat: DWORD;
var dwBufferSize: DWORD; var dwItemCount: DWORD;
ItemBuffer: PPDH_FMT_COUNTERVALUE_ITEM_A)
: PDH_STATUS; stdcall;
external 'PDH.DLL' name 'PdhGetFormattedCounterArrayA';
var
Query: HQUERY; // Запрос
Counter_Usage, Counter_PID: HCOUNTER; // Хендлы счетчков pid'ов и загрузки CPU
Initialized: Boolean; // Флаг нициализации PDH-запроса
function FillProcessesList(var slProcesses: TStringList): Boolean;
var
process: String; // Выходная строка
pid_size, usage_size, // Размеры буферов
pid_count, usage_count, // Количество элементов в полученных массивах
i: DWORD; // Счетчик цикла
pid_buf, usage_buf: PPDH_FMT_COUNTERVALUE_ITEM_A; // указатель на буферы
begin
Result := False;
if not Initialized then Exit;
// Получаем данные запроса
if PdhCollectQueryData(Query) <> ERROR_SUCCESS then Exit;
// Получаем размеры и количество полученных данных сетчиков
usage_size := 0; pid_size := 0;
PdhGetFormattedCounterArray(Counter_Usage,PDH_FMT_LARGE,usage_size,
usage_count,nil);
PdhGetFormattedCounterArray(Counter_PID,PDH_FMT_LONG,pid_size,
pid_count,nil);
// Выделяем память под буферы
usage_buf := AllocMem(usage_size);
pid_buf := AllocMem(pid_size);
try
// Пытаемся получить данные и выходим при неудаче, возвращая False.
if PdhGetFormattedCounterArray(Counter_Usage,PDH_FMT_LARGE,usage_size,
usage_count,usage_buf) <> ERROR_SUCCESS then Exit;
if PdhGetFormattedCounterArray(Counter_PID,PDH_FMT_LONG,pid_size,
pid_count,pid_buf) <> ERROR_SUCCESS then Exit;
if pid_count <> usage_count then Exit; // На всякий случай
for i := 0 to pid_count - 1 do
begin
// Пропускаем счетчик с общими данными
if PPDH_FMT_COUNTERVALUE_ITEM_A(DWORD(pid_buf) +
i * sizeof(PDH_FMT_COUNTERVALUE_ITEM_A)).szName = '_Total'
then Continue;
// Составляем выходную строку из данных счетчиков
process := 'Process: ' + PPDH_FMT_COUNTERVALUE_ITEM_A(DWORD(pid_buf) +
i * sizeof(PDH_FMT_COUNTERVALUE_ITEM_A)).szName;
process := process + ' - pid: '
+ IntToStr(PPDH_FMT_COUNTERVALUE_ITEM_A(DWORD(pid_buf) +
i * sizeof(PDH_FMT_COUNTERVALUE_ITEM_A)).FmtValue.largeValue);
process := process + ' - CPU usage: '
+ IntToStr(PPDH_FMT_COUNTERVALUE_ITEM_A(DWORD(usage_buf) +
i * sizeof(PDH_FMT_COUNTERVALUE_ITEM_A)).FmtValue.largeValue) + ' %';
slProcesses.Add(process);
end;
Result := True;
finally
// В любом случае освобождаем память
FreeMem(usage_buf);
FreeMem(pid_buf);
end;
end;
initialization
// При запуске программы:
// Открываем зпрос, добавляем в него счетчики и получаем начальные данные
if PdhOpenQuery(nil,0,Query) = ERROR_SUCCESS then
begin
PdhAddCounter(Query,PChar(PATH_USAGE),0,Counter_Usage);
PdhAddCounter(Query,PChar(PATH_PID),0,Counter_PID);
PdhCollectQueryData(Query);
Initialized := True;
end;
finalization
// При выходе из программы:
// Если был открыт запрос, то удаляем из него счетчики и закрываем его
if Initialized then
begin
PdhRemoveCounter(Counter_Usage);
PdhRemoveCounter(Counter_PID);
PdhCloseQuery(Query);
end;
end.
Для его использования наш демонстрационный проект не подойдет. Делай другой: на окне только Memo и таймер. Функцию FillProcessesList вызывай по таймеру.
Эта функция выдает ошибку, когда список процессов изменяется (запускается или закрывается программа). Так и должно быть, т.к. для вычисления загрузки процессора нужны два последовательных значения счетчика. Когда инфа для обновленного списка собирается в первый раз, то второго значения еще нет и функция выдает ошибку.
Если ты внимательно смотрел на рисуночек, то наверное заметил в нем аббревиатуру WMI. Это Windows Management Instrumentation – новая система мониторинга и контроля. Честно скажу, что с ней не разбирался, но принцип действия у нее схожий с PDH, только несколько сложнее. С этой системой удобнее работать на платформе .NET.
Назад |
(с) tripsin aka Орехов Роман, 2006 г.