1

How can I read the PhysicalDisk\Average Disk sec/Read and other similar performance counters, from Delphi?

I suspect that I should maybe be using RRUZ's WMI Class Generator, but I don't know which class I need, or how to use it.

Warren P
  • 65,725
  • 40
  • 181
  • 316

2 Answers2

3

This should get you started:

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

procedure Main;
var
  objWMIService: OleVariant;
  colItems: OleVariant;
  colItem: OleVariant;
  oEnum: IEnumvariant;
  iValue: LongWord;

  function GetWMIObject(const objectName: String): IDispatch;
  var
    chEaten: Integer;
    BindCtx: IBindCtx;
    Moniker: IMoniker;
  begin
    OleCheck(CreateBindCtx(0, BindCtx));
    OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten,
      Moniker));
    OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
  end;

begin
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems := objWMIService.ExecQuery
    ('Select * from Win32_perfformatteddata_perfdisk_LogicalDisk', 'WQL', 0);
  oEnum := IUnknown(colItems._NewEnum) as IEnumvariant;
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
    Writeln(colItem.Name + ', ' + IntToStr(colItem.AvgDiskSecPerRead));
  end;
end;

begin
  try
    CoInitialize(nil);
    try
      Main;
      Readln;
    finally
      CoUninitialize;
    end;
  except
    on E: Exception do
    begin
      Writeln(E.Classname, ': ', E.Message);
      Readln;
    end;
  end;
end.

I assume that you know enough about WMI to do the rest. The documentation for this WMI class is here: Win32_PerfFormattedData_PerfDisk_LogicalDisk.

If you want to build this on top of Rodrigo's libraries, then I refer you here: https://github.com/RRUZ/delphi-wmi-class-generator/blob/master/root_CIMV2/uWin32_PerfFormattedData_PerfDisk_LogicalDisk.pas

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
1

In case it might be helpful, you can also read the performance data directly. Here's a quick example using my PerfUtils and a translation of winperf.h from the JEDI API Library:

program test;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  JwaWinPerf,
  PerfUtils in 'PerfUtils.pas';

const
  CtrAvgDiskSecPerRead = 208;

procedure GetCounterAverageTimer(Obj: PPerfObjectType; NameIndex, InstanceIndex: Cardinal;
  out TimerValue, BaseValue: Cardinal);
var
  CounterTimer, CounterBase: PPerfCounterDefinition;
  Instance: PPerfInstanceDefinition;
begin
  TimerValue := 0;
  BaseValue := 0;
  // average timer counter
  CounterTimer := GetCounterByNameIndex(Obj, NameIndex);
  if not Assigned(CounterTimer) or (CounterTimer^.CounterType <> PERF_AVERAGE_TIMER) then
    Exit;
  // average base counter
  CounterBase := GetNextCounter(CounterTimer);
  if not Assigned(CounterBase) or (CounterBase^.CounterNameTitleIndex <> NameIndex) or
    (CounterBase^.CounterType <> PERF_AVERAGE_BASE) then
    Exit;
  Instance := GetInstance(Obj, InstanceIndex);
  if not Assigned(Instance) then
    Exit;
  TimerValue := GetCounterValue32(Obj, CounterTimer, Instance);
  BaseValue := GetCounterValue32(Obj, CounterBase, Instance);
end;

function GetPhysicalDiskAvgSecPerRead(DiskInstance: Integer; Data1, Data2: PPerfDataBlock): Double;
var
  Obj1, Obj2: PPerfObjectType;
  TimerValue1, TimerValue2, BaseValue1, BaseValue2: Cardinal;
begin
  Result := 0;
  Obj1 := GetObjectByNameIndex(Data1, ObjPhysicalDisk);
  Obj2 := GetObjectByNameIndex(Data2, ObjPhysicalDisk);
  if not Assigned(Obj1) or not Assigned(Obj2) then
    Exit;
  GetCounterAverageTimer(Obj1, CtrAvgDiskSecPerRead, DiskInstance, TimerValue1, BaseValue1);
  GetCounterAverageTimer(Obj2, CtrAvgDiskSecPerRead, DiskInstance, TimerValue2, BaseValue2);
  if (TimerValue2 = TimerValue1) or (BaseValue2 = BaseValue1) then
    Exit;

  Result := (Int64(TimerValue2 - TimerValue1) / Data1^.PerfFreq.QuadPart) / Int64(BaseValue2 - BaseValue1);
end;

function GetDiskInstanceName(DiskInstance: Integer; Data: PPerfDataBlock): WideString;
var
  Obj: PPerfObjectType;
  Instance: PPerfInstanceDefinition;
begin
  Result := '';
  Obj := GetObjectByNameIndex(Data, ObjPhysicalDisk);
  if not Assigned(Obj) then
    Exit;
  Instance := GetInstance(Obj, DiskInstance);
  if not Assigned(Instance) then
    Exit;

  SetString(Result, PWideChar(NativeUInt(Instance) + Instance^.NameOffset), Instance^.NameLength div SizeOf(WideChar));
end;

procedure Main;
var
  DiskInstance: Integer;
  Data1, Data2: PPerfDataBlock;
begin
  DiskInstance := 0; // first physical disk by default
  if ParamCount > 0 then
    DiskInstance := StrToInt(ParamStr(1));
  Data1 := GetPerformanceData(IntToStr(ObjPhysicalDisk));
  Writeln(Format('Disk instance %s', [GetDiskInstanceName(DiskInstance, Data1)]));
  repeat
    Sleep(1000);
    Data2 := GetPerformanceData(IntToStr(ObjPhysicalDisk));
    Writeln(Format('%.6f', [GetPhysicalDiskAvgSecPerRead(DiskInstance, Data1, Data2)]));
    Data1 := Data2;
  until False;
end;

begin
  try
    Main;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.
Community
  • 1
  • 1
Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128