Home > Delphi, WMI > WMI Tasks using Delphi – Disks and File Systems

WMI Tasks using Delphi – Disks and File Systems

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

Categories: Delphi, WMI
  1. October 25, 2011 at 4:34 pm | #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;
    
    • October 25, 2011 at 4:46 pm | #2

      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. October 25, 2011 at 4:51 pm | #3

    Thanks…

  3. October 25, 2011 at 7:17 pm | #4

    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;
    
    • October 25, 2011 at 7:59 pm | #5

      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.

  4. yxsoft
    October 25, 2011 at 9:48 pm | #6

    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?

    • October 26, 2011 at 8:42 pm | #7

      If the WMI service is stopped, the WMI operation will fail.

  5. October 26, 2011 at 12:36 pm | #8

    “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?

    • October 26, 2011 at 8:30 pm | #9

      Yes something like that, but I prefear use a class with all the logic encapsulated instead of global variables.

  6. Fcarvalho
    October 27, 2011 at 7:35 am | #10

    good and nice code!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 61 other followers