The Road to Delphi

Delphi – Free Pascal – Oxygene

WMI Tasks using Delphi – Disks and File Systems

10 Comments

How do I find out how much disk space each user is currently using on a computer?

If you are using disk quotas, then use the Win32_DiskQuota class and retrieve the values of the User and DiskSpaceUsed properties.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_DiskQuota','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Disk Space Used    %d',[Integer(FWbemObject.DiskSpaceUsed)]));
    Writeln(Format('Quota Volume       %s',[String(FWbemObject.QuotaVolume)]));
    Writeln(Format('User               %s',[String(FWbemObject.User)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine when a removable drive has been added to or removed from a computer?

Use a monitoring code that queries the Win32_VolumeChangeEvent class.

    function EventTypeStr(EventType:Integer):string;
    begin
       case EventType of
        1 : Result:='Configuration Changed';
        2 : Result:='Device Arrival';
        3 : Result:='Device Removal';
        4 : Result:='Docking';
       end;
    end;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
begin;
  Writeln('Press Ctrl-C to terminate');
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecNotificationQuery('SELECT * FROM Win32_VolumeChangeEvent');
  while true do
  begin
    FWbemObject := FWbemObjectSet.NextEvent;
    if not VarIsClear(FWbemObject) then
    begin
      Writeln(Format('Drive Name   %s',[String(FWbemObject.DriveName)]));
      Writeln(Format('Event Type   %s',[EventTypeStr(FWbemObject.EventType)]));
    end;
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine if a CD is in a CD-ROM drive?

Use the Win32_CDROMDrive class and the MediaLoaded property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_CDROMDrive','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID    %s',[String(FWbemObject.DeviceID)]));
    Writeln(Format('Media Loaded %s',[String(FWbemObject.MediaLoaded)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I distinguish between a fixed hard disk and a removable hard disk?

Use the Win32_LogicalDisk class and check the value of the DriveType property.

Value Meaning
0
Unknown
1
No Root Directory
2
Removable Disk
3
Local Disk
4
Network Drive
5
Compact Disc
6
RAM Disk
  function DriveTypeStr(DriveType:integer): string;
  begin
    case DriveType of
      0 : Result:='Unknown';
      1 : Result:='No Root Directory';
      2 : Result:='Removable Disk';
      3 : Result:='Local Disk';
      4 : Result:='Network Drive';
      5 : Result:='CD/DVD Disc';
      6 : Result:='RAM Disk';
    end;
  end;

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM  Win32_LogicalDisk','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID    %s',[String(FWbemObject.DeviceID)]));
    Writeln(Format('DriveType    %s',[DriveTypeStr(FWbemObject.DriveType)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine what file system is in use on a drive?

Use the Win32_LogicalDisk class and the FileSystem property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM  Win32_LogicalDisk','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID    %s',[String(FWbemObject.DeviceID)]));
    if not VarIsNull(FWbemObject.FileSystem) then
      Writeln(Format('File System  %s',[String(FWbemObject.FileSystem)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine how much free space is available on a drive?

Use the Win32_LogicalDisk class and the FreeSpace property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM  Win32_LogicalDisk','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID   %s',[String(FWbemObject.DeviceID)]));
    if not VarIsNull(FWbemObject.FreeSpace) then
      Writeln(Format('Free Space  %d',[Int64(FWbemObject.FreeSpace)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine the size of a drive?

Use the Win32_LogicalDisk class, and the Size property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM  Win32_LogicalDisk','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID   %s',[String(FWbemObject.DeviceID)]));
    if not VarIsNull(FWbemObject.Size) then
      Writeln(Format('Disk Size   %d',[Int64(FWbemObject.Size)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I find out what drives are mapped on a computer?

Use the Win32_MappedLogicalDisk class.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_MappedLogicalDisk','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Device ID   %s',[String(FWbemObject.DeviceID)]));
    Writeln(Format('Name        %s',[String(FWbemObject.Name)]));
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

How do I defragment a hard disk?

Use the Win32_Volume class and the Defrag method.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_Volume Where Name = "F:\\" ','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemObject.Defrag();
    FWbemObject:=Unassigned;
  end;
end;

How do I detect which drive letter is associated with a logical disk partition?

 

  1. Start with the Win32_DiskDrive class and query for instances of Win32_DiskPartition using the DeviceID property and the Win32_DiskDriveToDiskPartition association class. Now you have a collection of the partitions on the physical drive.
  2. Query for the Win32_LogicalDisk that represents the partition using the Win32_DiskPartition.DeviceID property and Win32_LogicalDiskToPartition association class.
  3. Get the drive letter from the Win32_LogicalDisk.DeviceID.

 

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator     : OLEVariant;
  FWMIService       : OLEVariant;
  wmiDiskDrives     : OLEVariant;
  wmiDiskPartitions : OLEVariant;
  wmiLogicalDisks   : OLEVariant;
  wmiDiskDrive      : OLEVariant;
  wmiDiskPartition  : OLEVariant;
  wmiLogicalDisk    : OLEVariant;
  oEnum             : IEnumvariant;
  oEnum2            : IEnumvariant;
  oEnum3            : IEnumvariant;
  iValue            : LongWord;
  DeviceID          : string;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  //Get the physical disk drive
  wmiDiskDrives := FWMIService.ExecQuery('SELECT Caption, DeviceID FROM Win32_DiskDrive','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(wmiDiskDrives._NewEnum) as IEnumVariant;
  while oEnum.Next(1, wmiDiskDrive, iValue) = 0 do
  begin
     //Use the disk drive device id to find associated partition
     DeviceID:=StringReplace(String(wmiDiskDrive.DeviceID),'\','\\',[rfReplaceAll]);
     wmiDiskPartitions := FWMIService.ExecQuery('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="'+DeviceID+'"} WHERE AssocClass = Win32_DiskDriveToDiskPartition','WQL',wbemFlagForwardOnly);
     oEnum2          := IUnknown(wmiDiskPartitions._NewEnum) as IEnumVariant;
     while oEnum2.Next(1, wmiDiskPartition, iValue) = 0 do
     begin
        wmiLogicalDisks := FWMIService.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="'+String(wmiDiskPartition.DeviceID)+'"} WHERE AssocClass = Win32_LogicalDiskToPartition','WQL',wbemFlagForwardOnly);
        oEnum3          := IUnknown(wmiLogicalDisks._NewEnum) as IEnumVariant;
        while oEnum3.Next(1, wmiLogicalDisk, iValue) = 0 do
        begin
          Writeln(Format('Drive letter associated with disk drive  %s %s Partition %s is %s',[String(wmiDiskDrive.Caption),String(wmiDiskDrive.DeviceID),String(wmiDiskPartition.DeviceID),String(wmiLogicalDisk.DeviceID)]));
          wmiLogicalDisk:=Unassigned;
        end;
       wmiDiskPartition:=Unassigned;
     end;
    wmiDiskDrive:=Unassigned;
    Writeln;
  end;
end;

This post is based in the MSDN entry WMI Tasks: Disks and File Systems

Author: Rodrigo

Just another Delphi guy.

10 thoughts on “WMI Tasks using Delphi – Disks and File Systems

  1. How do you specify a drive letter for a FWMIService.ExecQuery rather than selecting * all drives? For example:

    function WMIGetFreeDiskSpace( aDrive: string ): string;
    { Return the free disk space of a drive }
    const
      wbemFlagForwardOnly = $00000020;
    var
      FSWbemLocator : OLEVariant;
      FWMIService   : OLEVariant;
      FWbemObjectSet: OLEVariant;
      FWbemObject   : OLEVariant;
      oEnum         : IEnumvariant;
      iValue        : LongWord;
    begin;
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
    // specify a drive letter?
      FWbemObjectSet:= FWMIService.ExecQuery('SELECT ' + aDrive + ' FROM  Win32_LogicalDisk','WQL',wbemFlagForwardOnly);
      oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
      while oEnum.Next(1, FWbemObject, iValue) = 0 do
      begin
        //Result := Format('Device ID   %s',[String(FWbemObject.DeviceID)]);
        if not VarIsNull(FWbemObject.FreeSpace) then
          Result := Format('%s',[string(FWbemObject.FreeSpace)]);
        FWbemObject:=Unassigned;
      end;
    end;
    
    • try this

      function WMIGetFreeDiskSpace( const aDrive: string ): Int64;
      const
        wbemFlagForwardOnly = $00000020;
      var
        FSWbemLocator : OLEVariant;
        FWMIService   : OLEVariant;
        FWbemObjectSet: OLEVariant;
        FWbemObject   : OLEVariant;
        oEnum         : IEnumvariant;
        iValue        : LongWord;
      begin;
        FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
        FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
        FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT Size FROM Win32_LogicalDisk Where DeviceID="%s"',[aDrive]),'WQL',wbemFlagForwardOnly);
        oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
        if oEnum.Next(1, FWbemObject, iValue) = 0 then
        begin
          Result:=Int64(FWbemObject.Size);
          FWbemObject:=Unassigned;
        end;
      end;
      

      and use like this

      WMIGetFreeDiskSpace('C:')
      
  2. This function fails because while oEnum.Next( 1, FWbemObject, iValue ) 0.
    Why would it not be 0? WMIGetDriveTypei( aDrive ) = 5 returns true but oEnum.Next( 1, FWbemObject, iValue ) 0.

    function WMICDInDrive( aDrive: string ): string;
    { determine if a CD is in a CD-ROM drive }
    const
      wbemFlagForwardOnly = $00000020;
    var
      FSWbemLocator: OleVariant;
      FWMIService: OleVariant;
      FWbemObjectSet: OleVariant;
      FWbemObject: OleVariant;
      oEnum: IEnumvariant;
      iValue: LongWord;
      ullBoolean: boolean;
      ullBooleanS: string;
    begin
      // is drive a CD/DVD Disc
      if WMIGetDriveTypei( aDrive ) = 5 then
      begin
        FSWbemLocator := CreateOleObject( 'WbemScripting.SWbemLocator' );
        FWMIService := FSWbemLocator.ConnectServer( 'localhost', 'root\CIMV2', '', '' );
        FWbemObjectSet := FWMIService.ExecQuery( Format( 'SELECT MediaLoaded FROM Win32_CDROMDrive Where DeviceID="%s"',
            [ aDrive ] ), 'WQL', wbemFlagForwardOnly );
        oEnum := IUnknown( FWbemObjectSet._NewEnum ) as IEnumvariant;
        while oEnum.Next( 1, FWbemObject, iValue ) = 0 do
        begin
          ullBoolean := FWbemObject.MediaLoaded;
          ullBooleanS := BoolToYesNo( ullBoolean );
          Result := Format( '%s', [ ullBooleanS ] );
          FWbemObject := Unassigned;
        end;
      end
      else
        Result := 'Not CD/DVD Disc';
    end;
    
    • In the query against the Win32_CDROMDrive class you must use to compare the Drive property not DeviceID.

      So you must write a code like this

       FWbemObjectSet := FWMIService.ExecQuery( Format( 'SELECT MediaLoaded FROM Win32_CDROMDrive Where Drive="%s"',[ aDrive ] ), 'WQL', wbemFlagForwardOnly );
      

      Another advice, it seems which you are writting a set of helper functions to retrieve hardware information. If you want improve the performance use a single WMI connection and share that connection between the ExecQuery calls.

  3. If i call WMI at system startup period and WMI service is not ready, what will happen? Getting information failed or wait for WMI sevice ready and return ok automaticly?

  4. “If you want improve the performance use a single WMI connection and share that connection between the ExecQuery calls.” Is this what you mean:

    unit uWMI;
    
    interface
    
    uses Windows, Classes, ComCtrls, SysUtils, Variants, FileCtrl, ShellAPI, ShLwApi, ActiveX, ComObj;
    
    var
      FSWbemLocator: OleVariant;
      FWMIService: OleVariant;
    
    implementation
    
    function ConnectWithWMIServer: OleVariant;
    { Return WMI Server - Called once at program startup }
    var
      FSWbemLocator: OleVariant;
    begin
      FSWbemLocator := CreateOleObject( 'WbemScripting.SWbemLocator' );
      Result := FSWbemLocator.ConnectServer( 'localhost', 'root\CIMV2', '', '' );
    end;
    
    function WMICDInDrive( aDrive: string ): string;
    { Determine if a CD is in a CD-ROM drive }
    const
      wbemFlagForwardOnly = $00000020;
    var
      FWbemObjectSet: OleVariant;
      FWbemObject: OleVariant;
      oEnum: IEnumvariant;
      iValue: LongWord;
      ullBoolean: boolean;
      ullBooleanS: string;
    begin
      if not VarIsNull( FWMIService ) then
      begin
        // is drive a CD/DVD Disc
        if WMIGetDriveTypei( aDrive ) = 5 then
        begin
          FWbemObjectSet := FWMIService.ExecQuery( Format( 'SELECT MediaLoaded FROM Win32_CDROMDrive Where Drive="%s"',
              [ aDrive ] ), 'WQL', wbemFlagForwardOnly );
          oEnum := IUnknown( FWbemObjectSet._NewEnum ) as IEnumvariant;
          while oEnum.Next( 1, FWbemObject, iValue ) = 0 do
          begin
            ullBoolean := FWbemObject.MediaLoaded;
            ullBooleanS := BoolToYesNo( ullBoolean );
            Result := Format( '%s', [ ullBooleanS ] );
            FWbemObject := Unassigned;
          end;
        end
        else
          Result := 'Not CD/DVD Disc';
      end;
    end;
    
    //
    Usage:
    //
    procedure TFormMain.FormCreate( Sender: TObject );
    begin
       ...
      FWMIService := ConnectWithWMIServer;
    end;
    
    procedure TFormMain.PageControl1Change( Sender: TObject );
      begin
        case PageControl1.ActivePageIndex of
        ...
    10: // Disk
          begin
             CDInDrive1.Caption := 'CD in Drive E: ' + WMICDInDrive('E:');
          end;
    end;  //case
    end;
    

    I have all of your snippets in this unit as functions and procedures. At the moment everything works except two functions… When I finish would you like the demo and unit?

  5. good and nice code!

Leave a comment