Search code examples
comwmi

Iterating over SWbemPropertySet objects


A WQL query in WMI using the COM API returns a SWbemObjectSet. From this, we can use the ItemIndex method to iterate over all objects in the set, resulting in many SWbemObjects. These have a Properties_ property, which returns a SWbemPropertySet.

This object has a Count property, but no ItemIndex method, instead only an Item method, which takes a string name of the property to fetch. I'd like to iterate over all available properties, thus I don't have the name available. I've tried calling an ItemIndex method, but (as per the documentation) it does not exist on this object. There are many VBScript documents around that do something like For Each prop in object.Properties_, so I assume this is somehow possible.

How can I find all properties?


Solution

  • First, the ItemIndex property only exist in some versions of the Microsoft WMI Scripting Library you can find more info about this topic on this article which I wrote some time ago Be careful when you import the Microsoft WMI Scripting Library (The code uses Delphi but the same applies to any language using this Library).

    Now to iterate over the elements of an SWbemPropertySet you must get a instance to the enumerator accessing the _NewEnum property which provides a method for enumerating a collection of variants using the IEnumVARIANT interface. This is done automagically by vbscript, but in others languages like Delphi or C++ must be done manually.

    Try this Delphi sample

    {$APPTYPE CONSOLE}    
    
    uses
      SysUtils,
      ActiveX,
      ComObj,
      Variants,
      WbemScripting_TLB in 'WbemScripting_TLB.pas';
    
    procedure ShowProperties(const WMINameSpace, WMIClass : string);
    var
      WMIServices     : ISWbemServices;
      SWbemObjectSet  : ISWbemObjectSet;
      SObject         : ISWbemObject;
      LProperty       : ISWbemProperty;
      Enum, Enum2     : IEnumVariant;
      TempObj, TempObj2: OleVariant;
      Value           : Cardinal;
      SWbemPropertySet: ISWbemPropertySet;
    begin
      WMIServices := CoSWbemLocator.Create.ConnectServer('.', WMINameSpace,'', '', '', '', 0, nil);
      SWbemObjectSet := WMIServices.ExecQuery(Format('Select * FROM %s',[WMIClass]), 'WQL', 0, nil);
      Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;
      if (Enum.Next(1, TempObj, Value) = S_OK) then
      begin
        SObject     := IUnknown(TempObj) as ISWBemObject;
        SWbemPropertySet := SObject.Properties_;
        Enum2 := (SWbemPropertySet._NewEnum) as IEnumVariant;
        while (Enum2.Next(1, TempObj2, Value) = S_OK) do
        begin
          LProperty     := IUnknown(TempObj2) as ISWbemProperty;
          Writeln(LProperty.Name);
          TempObj2:=Unassigned;
        end;
        TempObj:=Unassigned;
      end;
    end;
    
    begin
     try
        CoInitialize(nil);
        try
          ShowProperties('root\cimv2','Win32_BaseBoard');
        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.
    

    I hope which you can translate this sample to the Go language.