страничка tripsin'а


Главная | Статьи | Заметки | Файлы| Ссылки | я
Назад

Дальше

Получение списка процессов. 5 часть. PDH.

Скачай демонстрационный проект (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 г.

Hosted by uCoz