如何从 Delphi 读取 PerfMon PhysicalDisk\Average 磁盘 sec/Read 计数器?

How can I read PerfMon PhysicalDisk\Average Disk sec/Read counter from Delphi?

如何从 Delphi 中读取 PhysicalDisk\Average Disk sec/Read 和其他类似的性能计数器?

我怀疑我应该使用 RRUZ 的 WMI Class 生成器,但我不知道我需要哪个 class 或如何使用它。

这应该让你开始:

{$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.

我假设您对 WMI 有足够的了解,可以完成剩下的工作。此 WMI class 的文档位于:Win32_PerfFormattedData_PerfDisk_LogicalDisk

如果你想在 Rodrigo 的库之上构建它,那么我在这里推荐你:https://github.com/RRUZ/delphi-wmi-class-generator/blob/master/root_CIMV2/uWin32_PerfFormattedData_PerfDisk_LogicalDisk.pas

如果有帮助,您也可以直接阅读性能数据。这是一个使用我的 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.