Recent

Author Topic: Hard disk monitor  (Read 6097 times)

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Hard disk monitor
« on: February 15, 2019, 02:13:00 pm »
Good morning friends
I need your help, how can I get the use of the hard drive?

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Hard disk monitor
« Reply #1 on: February 15, 2019, 02:36:39 pm »
Can you be a little more specific about what you want to do?

If you just want to check the space used/free then use SysUtils.DiskSize(), SysUtils.DiskFree(), etc.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #2 on: February 15, 2019, 03:06:00 pm »
hello friend, I was referring to get the use of the hard drive in activity, not the space, the work of the hard disk is what I need, I attach an example image.

Wallaby

  • Jr. Member
  • **
  • Posts: 78
Re: Hard disk monitor
« Reply #3 on: February 15, 2019, 03:58:20 pm »
You might need to access performance counters.

It won't be easy...

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: Hard disk monitor
« Reply #4 on: February 15, 2019, 04:44:45 pm »
Just as a minimum project... Put a TTimer and an TMemo on a form and Interval of the TTimer to 500 with this event:

Code: Pascal  [Select][+][-]
  1. uses contnrs, utilwmi;
  2.  
  3. procedure TForm1.Timer1Timer(Sender: TObject);
  4. var
  5.   WMIResult: TFPObjectList;
  6.   I: integer;
  7.   PropNamesIndex: integer;
  8.   PropNames: array[0..1] of string = ('Name', 'PercentDiskTime');
  9. begin
  10.   WMIResult := GetWMIInfo('win32_perfformatteddata_perfdisk_physicaldisk',
  11.     PropNames, ''); // you could also do WHERE Name=''_Total'' as last param
  12.  
  13.   Memo1.Clear;
  14.   for I := 0 to Pred(WMIResult.Count) do
  15.   begin
  16.     Memo1.Lines.Add('================================================');
  17.     for PropNamesIndex := Low(PropNames) to High(PropNames) do
  18.     begin
  19.       Memo1.Lines.Add(TStringList(WMIResult[i]).Names[PropNamesIndex] +
  20.         ' : ' + TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex]);
  21.     end;
  22.   end;
  23.   // Clean up
  24.   WMIResult.Free;
  25. end;

Get the utilwmi_v3_official.7z from the following topic and extract wmiutil.pas to your project directory.
https://forum.lazarus.freepascal.org/index.php/topic,24490.msg232201.html#msg232201

It's just to show you the wmi performance counters from all harddisks. If you add the WHERE Name='_Total' you get the average number (but maybe only take the avarage of the physical harddisks because the _Total has some extra). And you can also take other numbers from the wmi.

(not sure why the percentage sometimes comes above 100% when I run a benchmark on disk D)

Quote
================================================
Name : 0 D:
PercentDiskTime : 96
================================================
Name : 1 C:
PercentDiskTime : 0
================================================
Name : 2
PercentDiskTime : 0
================================================
Name : 3
PercentDiskTime : 0
================================================
Name : 4
PercentDiskTime : 0
================================================
Name : 5
PercentDiskTime : 0
================================================
Name : 6
PercentDiskTime : 0
================================================
Name : 7
PercentDiskTime : 0
================================================
Name : _Total
PercentDiskTime : 12

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #5 on: February 15, 2019, 11:30:56 pm »
thank you very much friend, I will do some tests and I will comment  :)

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #6 on: February 16, 2019, 04:16:18 am »
Good friends, based on what I found for an almost exact result, "PercentIdleTime" should be used instead of "PercentDiskTime".
here the source from which I took it:

https://social.msdn.microsoft.com/Forums/windows/en-US/1bafe69f-5b77-444f-9fb3-9e3796739e35/why-the-values-of-percentdisktime-and-percentidletime-were-total-different-in-wmi?forum=windowsgeneraldevelopmentissues
http://blog.gulfsoft.com/2008/03/percent-disk-time-from-windows-perfmon.html

I have modified the code in the following way and it is working well for me at the moment:

Code: Pascal  [Select][+][-]
  1. uses   contnrs, utilwmi;
  2.  
  3. procedure TForm1.Timer1Timer(Sender: TObject);
  4. var
  5.   WMIResult: TFPObjectList;
  6.   I: integer;
  7.   PropNamesIndex: integer;
  8.   PropNames: array[0..1] of string = ('Name', 'PercentIdleTime');
  9. begin
  10.   Application.ProcessMessages;
  11.   WMIResult := GetWMIInfo('win32_perfformatteddata_perfdisk_physicaldisk',
  12.     PropNames, 'WHERE Name="_Total"'); // you could also do WHERE Name=''_Total'' as last param     ,   Work fine  'WHERE Name="_Total"'
  13.  
  14.   Memo1.Clear;
  15.   for I := 0 to Pred(WMIResult.Count) do
  16.   begin
  17.     Memo1.Lines.Add('=========================');
  18.     for PropNamesIndex := Low(PropNames) to High(PropNames) do
  19.     begin
  20.       if TStringList(WMIResult[i]).Names[PropNamesIndex]='PercentIdleTime' then
  21.       begin
  22.       {Memo1.Lines.Add(TStringList(WMIResult[i]).Names[PropNamesIndex] +
  23.         ' - ' + TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex]);}
  24.       //' - ' + inttostr(100-strtoint(TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex])));
  25.  
  26.       Memo1.Lines.Add(inttostr(100-strtoint(TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex])));
  27.       ProgressBar1.Position:=100-strtoint(TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex]);
  28.       Label1.Caption:=inttostr(100-strtoint(TStringList(WMIResult[i]).ValueFromIndex[PropNamesIndex]))+'%';
  29.       end;
  30.     end;
  31.   end;
  32.   // Clean up
  33.   WMIResult.Free;
  34. end;

for the moment it is working well, but I will continue to do tests  :)


Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #7 on: February 17, 2019, 05:54:56 am »
Friends a question, how to make these codes do not freeze my application  :-\

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Hard disk monitor
« Reply #8 on: February 17, 2019, 08:40:58 am »
Friends a question, how to make these codes do not freeze my application  :-\
I have in Windows 7 this code does not freeze the application
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     Timer1: TTimer;
  13.     procedure FormCreate(Sender: TObject);
  14.     procedure Timer1Timer(Sender: TObject);
  15.   private
  16.     FWbemLocator: OleVariant;
  17.     FWMIService: OleVariant;
  18.     function GetDiskPerfUsedPercent: Integer;
  19.   end;
  20.  
  21. var
  22.   Form1: TForm1;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. uses ActiveX, ComObj;
  29.  
  30. function TForm1.GetDiskPerfUsedPercent: Integer;
  31. const
  32.   WBEM_FLAG_FORWARD_ONLY = $20;
  33.   WBEM_RETURN_IMMEDIATELY = $10;
  34.   CFlags = WBEM_FLAG_FORWARD_ONLY or WBEM_RETURN_IMMEDIATELY;
  35.   CQuery = 'select PercentIdleTime ' +
  36.     'from Win32_PerfFormattedData_PerfDisk_PhysicalDisk ' +
  37.     'where Name="_Total"';
  38. var
  39.   Enum: IEnumVARIANT;
  40.   WbemResult: OleVariant;
  41.   WbemCollection: OleVariant;
  42. begin
  43.   Result := -1;
  44.   WbemCollection := FWMIService.ExecQuery(CQuery, 'WQL', CFlags);
  45.   Enum := IUnknown(WbemCollection._NewEnum) as IEnumVariant;
  46.   if Enum.Next(1, WbemResult, PLongWord(nil)^) = 0 then
  47.     Result := 100 - WbemResult.Properties_.Item('PercentIdleTime').Value;
  48. end;
  49.  
  50. procedure TForm1.FormCreate(Sender: TObject);
  51. begin
  52.   FWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  53.   FWMIService := FWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  54. end;
  55.  
  56. procedure TForm1.Timer1Timer(Sender: TObject);
  57. begin
  58.   Caption := IntToStr(GetDiskPerfUsedPercent);
  59. end;
  60.  
  61. end.

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #9 on: February 18, 2019, 12:35:51 am »
Greetings friend, thank you very much for sharing  :), all the code works perfect, my problem is this:
the application can move normally, but every second of the timer freezes a little, it's not much, but I would like the application to be able to move normally.

PS: I just tried it on win7x86 and win10x64

help me please  :(
« Last Edit: February 18, 2019, 12:40:07 am by Ericktux »

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: Hard disk monitor
« Reply #10 on: February 18, 2019, 12:24:47 pm »
the application can move normally, but every second of the timer freezes a little, it's not much, but I would like the application to be able to move normally.
This is normal. The code is executed in the main thread, so that main thread is momentarily busy at the moment that code is executed. You'll get that with every code. The TTimer solution somewhat eliminated that but the code is still executed in the same thread.

If you really want smooth execution, you'll need to move that code to a new thread.

Something like this (thrown together so not thoroughly tested):

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   TShowStatusEvent = procedure(Status: string) of object;
  12.  
  13.   TDiskPerfUsedThread = class(TThread)
  14.   private
  15.     FWbemLocator: olevariant;
  16.     FWMIService: olevariant;
  17.     fStatusText: string;
  18.     FOnShowStatus: TShowStatusEvent;
  19.     function GetDiskPerfUsedPercent: integer;
  20.     procedure ShowStatus;
  21.   protected
  22.     procedure Execute; override;
  23.   public
  24.     constructor Create(CreateSuspended: boolean);
  25.     property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
  26.   end;
  27.  
  28. type
  29.  
  30.   { TForm1 }
  31.  
  32.   TForm1 = class(TForm)
  33.     Button1: TButton;
  34.     Label1: TLabel;
  35.     procedure FormCreate(Sender: TObject);
  36.     procedure FormDestroy(Sender: TObject);
  37.  
  38.   public
  39.     MyThread: TDiskPerfUsedThread;
  40.     procedure ShowStatus(Status: string);
  41.   end;
  42.  
  43. var
  44.   Form1: TForm1;
  45.  
  46. implementation
  47.  
  48. uses ActiveX, ComObj;
  49.  
  50. {$R *.lfm}
  51.  
  52. { TForm1 }
  53. constructor TDiskPerfUsedThread.Create(CreateSuspended: boolean);
  54. begin
  55.   inherited Create(CreateSuspended);
  56.   FreeOnTerminate := True;
  57. end;
  58.  
  59. function TDiskPerfUsedThread.GetDiskPerfUsedPercent: integer;
  60. const
  61.   WBEM_FLAG_FORWARD_ONLY = $20;
  62.   WBEM_RETURN_IMMEDIATELY = $10;
  63.   CFlags = WBEM_FLAG_FORWARD_ONLY or WBEM_RETURN_IMMEDIATELY;
  64.   CQuery = 'select PercentIdleTime ' +
  65.     'from Win32_PerfFormattedData_PerfDisk_PhysicalDisk ' + 'where Name="_Total"';
  66. var
  67.   Enum: IEnumVARIANT;
  68.   WbemResult: olevariant;
  69.   WbemCollection: olevariant;
  70. begin
  71.   Result := -1;
  72.   WbemCollection := FWMIService.ExecQuery(CQuery, 'WQL', CFlags);
  73.   Enum := IUnknown(WbemCollection._NewEnum) as IEnumVariant;
  74.   if Enum.Next(1, WbemResult, PLongWord(nil)^) = 0 then
  75.     Result := 100 - WbemResult.Properties_.Item('PercentIdleTime').Value;
  76. end;
  77.  
  78. procedure TDiskPerfUsedThread.ShowStatus;
  79. begin
  80.   if Assigned(FOnShowStatus) then
  81.   begin
  82.     FOnShowStatus(fStatusText);
  83.   end;
  84. end;
  85.  
  86. procedure TDiskPerfUsedThread.Execute;
  87. begin
  88.   CoInitializeEx(nil, COINIT_MULTITHREADED);
  89.   FWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  90.   FWMIService := FWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  91.   fStatusText := 'TThread Starting...';
  92.   Synchronize(@Showstatus);
  93.   fStatusText := 'TThread Running...';
  94.   while (not Terminated) do
  95.   begin
  96.     fStatusText := Format('%d', [GetDiskPerfUsedPercent]);
  97.     Synchronize(@Showstatus);
  98.   end;
  99. end;
  100.  
  101. procedure TForm1.FormCreate(Sender: TObject);
  102. begin
  103.   MyThread := TDiskPerfUsedThread.Create(True);
  104.   MyThread.OnShowStatus := @ShowStatus;
  105.   MyThread.Start;
  106. end;
  107.  
  108. procedure TForm1.ShowStatus(Status: string);
  109. begin
  110.   Label1.Caption := Status;
  111. end;
  112.  
  113. procedure TForm1.FormDestroy(Sender: TObject);
  114. begin
  115.   MyThread.Terminate;
  116. end;
  117.  
  118. end.

By the way. My disk-percentage is wrong with this example because for me, there are some network drives mapped to driveletters and I think the _Total number also takes those to calculate the average (which the taskmanager doesn't do). So you might only want to use the physical disks to calculate an average.

Ericktux

  • Sr. Member
  • ****
  • Posts: 345
Re: Hard disk monitor
« Reply #11 on: February 18, 2019, 04:51:50 pm »
thank you very much rvk friend for your support, I will try it and tell you how it went  :)

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Hard disk monitor
« Reply #12 on: February 18, 2019, 05:06:28 pm »
If you really want smooth execution, you'll need to move that code to a new thread.
Good idea!
It is also possible to do this without a new class and with a timeout to reduce the load on the system:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     procedure FormCreate(Sender: TObject);
  13.     procedure FormDestroy(Sender: TObject);
  14.   private
  15.     FWakeUpEvent: PRTLEvent;
  16.     FWorkThread: TThread;
  17.     FStopWork: Boolean;
  18.     procedure CollectDiskUsage(ReportStatus: TThreadReportStatus);
  19.     procedure ShowDiskUsageStatus(Sender: TThread; const Status: string);
  20.   end;
  21.  
  22. var
  23.   Form1: TForm1;
  24.  
  25. implementation
  26.  
  27. uses ComObj, ActiveX;
  28.  
  29. {$R *.lfm}
  30.  
  31. procedure TForm1.FormCreate(Sender: TObject);
  32. begin
  33.   FWakeUpEvent := RTLEventCreate;
  34.   FWorkThread := TThread.ExecuteInThread(@CollectDiskUsage, @ShowDiskUsageStatus);
  35. end;
  36.  
  37. procedure TForm1.FormDestroy(Sender: TObject);
  38. begin
  39.   FStopWork := True;
  40.   RTLEventSetEvent(FWakeUpEvent); // Wakeup thread if it wait
  41.   FWorkThread.WaitFor; // work ended
  42.   RTLEventDestroy(FWakeUpEvent);
  43. end;
  44.  
  45. procedure TForm1.CollectDiskUsage(ReportStatus: TThreadReportStatus);
  46.  
  47.   function GetStatus(const WMIService: OleVariant): string;
  48.   const
  49.     WBEM_FLAG_FORWARD_ONLY = $20;
  50.     WBEM_RETURN_IMMEDIATELY = $10;
  51.     CFlags = WBEM_FLAG_FORWARD_ONLY or WBEM_RETURN_IMMEDIATELY;
  52.     CQuery = 'select PercentIdleTime ' +
  53.       'from Win32_PerfFormattedData_PerfDisk_PhysicalDisk ' + 'where Name="_Total"';
  54.   var
  55.     Enum: IEnumVARIANT;
  56.     WbemResult: OleVariant;
  57.     WbemCollection: OleVariant;
  58.   begin
  59.     Result := '';
  60.     WbemCollection := WMIService.ExecQuery(CQuery, 'WQL', CFlags);
  61.     Enum := IUnknown(WbemCollection._NewEnum) as IEnumVariant;
  62.     if Enum.Next(1, WbemResult, PLongWord(nil)^) = 0 then
  63.       Result := IntToStr(100 - WbemResult.Properties_.Item('PercentIdleTime').Value);
  64.   end;
  65.  
  66. var
  67.   WbemLocator: OleVariant;
  68.   WMIService: OleVariant;
  69. begin
  70.   ReportStatus('Thread starting...');
  71.   CoInitializeEx(nil, COINIT_MULTITHREADED);
  72.   WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  73.   WMIService := WbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  74.   repeat
  75.     ReportStatus(GetStatus(WMIService));
  76.     RTLEventWaitFor(FWakeUpEvent, 1 * MSecsPerSec); // Sleep
  77.   until FStopWork;
  78. end;
  79.  
  80. procedure TForm1.ShowDiskUsageStatus(Sender: TThread; const Status: string);
  81. begin
  82.   Caption := Status;
  83. end;
  84.  
  85. end.

Quote
My disk-percentage is wrong this with this example because for me, there are some network drives mapped to driveletters and I think the _Total number also takes those to calculate the average (which the taskmanager doesn't do). So you might only want to use the physical disks to calculate an average.
Very strange, because the name of Wmi class contains PhysicalDisk. Does this powershell command show your network drives?
Code: C  [Select][+][-]
  1. (Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk).Name

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: Hard disk monitor
« Reply #13 on: February 18, 2019, 05:18:49 pm »
Neat, I've never used Thread.ExecuteInThread() but that seems a lot easier  8-)

Quote
My disk-percentage is wrong this with this example because for me, there are some network drives mapped to driveletters and I think the _Total number also takes those to calculate the average (which the taskmanager doesn't do). So you might only want to use the physical disks to calculate an average.
Very strange, because the name of Wmi class contains PhysicalDisk. Does this powershell command show your network drives?
Code: C  [Select][+][-]
  1. (Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk).Name
I'm not sure what the drives are, that I'm getting back but the result is:
Quote
PS C:\Users\Rik> (Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk).Name
0 D:
1 C:
2
3
4
5
6
7
_Total
PS C:\Users\Rik>

I have a physical C and D, a DVD E and G and a Subst Q
and further network F, S, U, V, X and Y

That's why I thought the 6 extra drives would be the network mappings (although Win32_PerfFormattedData_PerfDisk_PhysicalDisk name would suggest otherwise)

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Hard disk monitor
« Reply #14 on: February 18, 2019, 05:38:24 pm »
I have a physical C and D, a DVD E and G and a Subst Q
and further network F, S, U, V, X and Y
That's why I thought the 6 extra drives would be the network mappings (although Win32_PerfFormattedData_PerfDisk_PhysicalDisk name would suggest otherwise)
It turns out that they really are considered as a physical disk drive. More details can be found in the query:
Code: C  [Select][+][-]
  1. Get-CimInstance Win32_DiskDrive | Format-List *

 

TinyPortal © 2005-2018