The Road to Delphi

Delphi – Free Pascal – Oxygene

How obtain the source of the WMI Data

4 Comments

We normally use the WMI to either return information about software and hardware installed, but you probably ever wondered from where this data is obtained?. Well the answer is, this data is obtained from many sources like the WinApi, the Windows Registry, the SMBIOS or custom functions embedded inside of the MOF definition.

WMI Metadata

All the WMI classes have a very rich set of metadata which defines the properties names, data types, descriptions and also each class and property have a special type of attribute called qualifiers which contain addtional metadata information about the WMI element ,within these qualifiers there is one called MappingStrings.

The MSDN documentation about this qualifier states

MappingStrings : Set of values that indicate a path to a location where you can find more information about the origin of a property, class, association, indication, or reference. The mapping string can be a directory path, a URL, a registry key, an include file, reference to a CIM class, or some other format.

This means that by analyzing the content of the MappingStrings qualifier you can determine the source of the data or obtain extra information about this property. Let me explain with a sample. The Win32_DiskDrive WMI class provides information about the physical disks present in the system like Bytes Per Sector, Firmware Revision, Interface Type (SCSI, IDE, USB) and so on. Now if you analize the MappingStrings qualifier of these properties you can determine where is located the information of the disks in the system.

The Code

Check the next function to access the MappingStrings qualifier (if exist) from any WMI class.

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

procedure  GetWmiPropsSources(Const NameSpace, ClassName: string);
const
  wbemFlagUseAmendedQualifiers = $00020000;
Var
  Properties        : OleVariant;
  Qualifiers        : OleVariant;
  rgvarProp         : OleVariant;
  rgvarQualif       : OleVariant;
  objSWbemLocator   : OleVariant;
  objSWbemObjectSet : OleVariant;
  objWMIService     : OleVariant;
  EnumProps         : IEnumVariant;
  EnumQualif        : IEnumVariant;
  pceltFetched      : Cardinal;
  Lindex            : Integer;
begin
    //create the WMI Scripting object
    objSWbemLocator  := CreateOleObject('WbemScripting.SWbemLocator');
    //connect to the WMi service on the local mahicne
    objWMIService    := objSWbemLocator.ConnectServer('localhost', NameSpace, '', '');
    //get the metadata of the WMI class
    objSWbemObjectSet:= objWMIService.Get(ClassName, wbemFlagUseAmendedQualifiers);
    //get a pointer to the properties
    Properties := objSWbemObjectSet.Properties_;
    //get an enumerator to the properties
    EnumProps         := IUnknown(Properties._NewEnum) as IEnumVariant;
    //iterate over the properties
    while EnumProps.Next(1, rgvarProp, pceltFetched) = 0 do
    begin
      //get a pointer to the qualifiers of the current property
      Qualifiers      := rgvarProp.Qualifiers_;
      //get an enumerator to the qualifiers
      EnumQualif     := IUnknown(Qualifiers._NewEnum) as IEnumVariant;
      //iterate over the qualifiers
      while EnumQualif.Next(1, rgvarQualif, pceltFetched) = 0 do
      begin
        //check the name of the qualifier
        if SameText('MappingStrings',rgvarQualif.Name) then
        begin
           Writeln(rgvarProp.Name);
           //write the value of the qualifier
           if not VarIsNull(rgvarQualif.Value) and  VarIsArray(rgvarQualif.Value) then
            for Lindex := VarArrayLowBound(rgvarQualif.Value, 1) to VarArrayHighBound(rgvarQualif.Value, 1) do
              Writeln(Format('  %s',[String(rgvarQualif.Value[Lindex])]));
        end;
        rgvarQualif:=Unassigned;
      end;
      rgvarProp:=Unassigned;
    end;
end;


begin
 try
    CoInitialize(nil);
    try
      GetWmiPropsSources('root\cimv2', 'Win32_DiskDrive');
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.

After of execute the above code you will get a result like this

Availability
  MIF.DMTF|Operational State|003.5
  MIB.IETF|HOST-RESOURCES-MIB.hrDeviceStatus
BytesPerSector
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|BytesPerSector
Capabilities
  MIF.DMTF|Storage Devices|001.9
  MIF.DMTF|Storage Devices|001.11
  MIF.DMTF|Storage Devices|001.12
  MIF.DMTF|Disks|003.7
DeviceID
  WMI
FirmwareRevision
  Win32API|Device Input and Output Structures|STORAGE_DEVICE_DESCRIPTOR|ProductRevisionOffset
Index
  Win32API|Windows 95/98 Functions|DRIVE_MAP_INFObtInt13Unit
InstallDate
  MIF.DMTF|ComponentID|001.5
InterfaceType
  Win32API|Device Input and Output Functions|DeviceIoControl
Manufacturer
  Win32Registry|HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port\Scsi Bus\Target Id\Logical Unit
 Id\Identifier
  Win32Registry|Manufacturer
MaxMediaSize
  MIF.DMTF|Sequential Access Devices|001.2
MediaLoaded
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|MediaType|FixedMedia
MediaType
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|MediaType
Model
  Win32Registry|HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port\Scsi Bus\Target Id\Logical Unit
 Id\Identifier
  Win32Registry|ProductId
Partitions
  Win32API|Device Input and Output Structures|PARTITION_INFORMATION|RecognizedPartition
SCSIBus
  Win32API|Device Input and Output Structures|SCSI_ADDRESS|PathId
SCSILogicalUnit
  Win32API|Device Input and Output Structures|SCSI_ADDRESS|Lun
SCSIPort
  Win32API|Device Input and Output Structures|SCSI_ADDRESS|PortNumber
SCSITargetId
  Win32API|Device Input and Output Structures|SCSI_ADDRESS|TargetId
SectorsPerTrack
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|SectorsPerTrack
SerialNumber
  Win32API|Device Input and Output Structures|STORAGE_DEVICE_DESCRIPTOR|SerialNumberOffset
Signature
  Win32API|Device Input and Output Structures|DRIVE_LAYOUT_INFORMATION|Signature
Size
  Win32API|Device Input and Output Structures|DISK_GEOMETRY
StatusInfo
  MIF.DMTF|Operational State|003.3
TotalCylinders
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|Cylinders
TotalHeads
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|TracksPerCylinder
TotalSectors
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|SectorsPerTrack
TotalTracks
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|TracksPerCylinder
TracksPerCylinder
  Win32API|Device Input and Output Structures|DISK_GEOMETRY|TracksPerCylinder

As you can see most of the properties of this class have a MappingStrings qualifier which shows the source (Win32API, Win32Registry, MIF.DMTF) of the data.

Finally if you are interested on this topic take a look to the WMI Delphi Code Creator which includes this feature.

Author: Rodrigo

Just another Delphi guy.

4 thoughts on “How obtain the source of the WMI Data

  1. Pingback: /*Prog*/ Delphi-Neftalí /*finProg*/ » Una quincena más… (11/04/2012)

  2. hot stuff, much thX !!!!!!

  3. I have read that the WMI can be used to read the CPU Thermometer. Could you give a sample of how this could be done?

Leave a comment