The Road to Delphi

Delphi – Free Pascal – Oxygene

Accesing the WMI from Object Pascal Code (Delphi, Oxygene, FreePascal)

13 Comments

Due to many developers don’t know how access the WMI from Pascal code, I decided to write this entry to show a simple set of samples using Delphi Win32, Delphi.Net, Oxygene and Free-pascal.

Delphi .Net

Delphi Prism

Delphi win32 importing the Microsoft WMIScripting Library.

Delphi win32 Late-binding.

Lazarus Late-binding

UPDATE

To access the WMI using COM  you must check this article

Accesing the WMI from Delphi and FPC via COM (without late binding or WbemScripting_TLB)

All the code samples uses the Win32_BaseBoard wmi class and are console applications just for simplicity.

The code showed in this entry can be optimized a lot for experienced developers, these simple  piece of code only pretend give a basic idea which how connect to the  WMi service and retieve data using a WQL query.

This is the Managed Object Format (MOF) representation of this class.

class Win32_BaseBoard : CIM_Card
{
  string   Caption;
  string   ConfigOptions[];
  string   CreationClassName;
  real32   Depth;
  string   Description;
  real32   Height;
  boolean  HostingBoard;
  boolean  HotSwappable;
  datetime InstallDate;
  string   Manufacturer;
  string   Model;
  string   Name;
  string   OtherIdentifyingInfo;
  string   PartNumber;
  boolean  PoweredOn;
  string   Product;
  boolean  Removable;
  boolean  Replaceable;
  string   RequirementsDescription;
  boolean  RequiresDaughterBoard;
  string   SerialNumber;
  string   SKU;
  string   SlotLayout;
  boolean  SpecialRequirements;
  string   Status;
  string   Tag;
  string   Version;
  real32   Weight;
  real32   Width;
};

Using Delphi Win32 and importing the Microsoft WMIScripting Library

This must be the most used method for accessing the WMI from Delphi Win32, basically consist in import the Microsoft WMIScripting Library and then delphi will create a wrapper containing all the types, const and enumerations to access the WMI, the main objects(interfaces) are ISWbemServices (which create a connection with WMI Windows Service) , ISWbemObjectSet (Execute a WQL Query) and ISWbemPropertySet

Check the picture to see the Microsoft Wmi Scripting library organization.

The basics steps are

1) import the Microsoft WMIScripting Library

2) Establish a connection with the WMI service in a local o remote computer. using the TSWbemLocator.ConnectServer function.

    function ConnectServer(const strServer: WideString; const strNamespace: WideString;
                           const strUser: WideString; const strPassword: WideString;
                           const strLocale: WideString; const strAuthority: WideString;
                           iSecurityFlags: Integer; const objWbemNamedValueSet: IDispatch): ISWbemServices;

 

SWbemServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);

3) Execute your WQL Query using the ISWbemServices.ExecQuery function which returns a instance to the ISWbemObjectSet object.

this is the declaration of the ISWbemServices.ExecQuery

    function ExecQuery(const strQuery: WideString; const strQueryLanguage: WideString;
                       iFlags: Integer; const objWbemNamedValueSet: IDispatch): ISWbemObjectSet; safecall;

and this is a sample of calling this function

SWbemObjectSet  := SWbemServices.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL', 0, nil);

4) the next step is iterate over the returned data, to do this we can use the ISWbemObjectSet._NewEnum property wich return an enumerator to the collection returned by the SWbemObjectSet

so using a IEnumVariant (which is part of the ActiveX unit) variable we can obtain the enumerator to the collection in this way.

Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;

5) finally we need access the properties of the current collection, using the SWbemObject.Properties_.Item function

    while (Enum.Next(1, TempObj, Value) = S_OK) do
    begin
       SWbemObject     := IUnknown(tempObj) as ISWBemObject;
       SWbemPropertySet:= SWbemObject.Properties_;
       SWbemPropertySet.Item('SerialNumber', 0).Get_Value;//Get the value of the SerialNumber property
    end;

Advantages of importing the Microsoft WMIScripting Library

1) Full access through the IDE (code-completion) to the types, enumerations and constants of the WMIScripting Library.
2) easy to debug syntax errors which would have been missed had you used late binding.

Drawbacks

1) Depending of the Windows version which you uses, you can get different results when you import the WMIScripting Library, check this link.

2) The final exe size is incremented when you import this library.

Check the full sample source code to get a idea how use this method.

program WmiDelphiWin32_Tlb;

{$APPTYPE CONSOLE}

uses
  ActiveX,
  Variants,
  SysUtils,
  WbemScripting_TLB in '..\..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';//

 function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

Procedure GetWin32_BaseBoardInfo;
var
  SWbemServices   : ISWbemServices;
  SWbemObjectSet  : ISWbemObjectSet;
  Item            : Variant;
  Enum            : IEnumVariant;
  TempObj         : OleVariant;
  Value           : Cardinal;
  SWbemObject     : ISWbemObject;
  SWbemPropertySet: ISWbemPropertySet;
begin
  SWbemServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  SWbemObjectSet  := SWbemServices.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL', 0, nil);
  Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;

    while (Enum.Next(1, TempObj, Value) = S_OK) do
    begin
       SWbemObject     := IUnknown(tempObj) as ISWBemObject;
       SWbemPropertySet:= SWbemObject.Properties_;

        Writeln(Format('Caption                   %s',[VarStrNull(SWbemPropertySet.Item('Caption', 0).Get_Value)]));// String
        Writeln(Format('ConfigOptions             %s',[VarStrNull(SWbemPropertySet.Item('ConfigOptions', 0).Get_Value)]));// String
        Writeln(Format('CreationClassName         %s',[VarStrNull(SWbemPropertySet.Item('CreationClassName', 0).Get_Value)]));// String
        Writeln(Format('Depth                     %s',[VarStrNull(SWbemPropertySet.Item('Depth', 0).Get_Value)]));// Real32
        Writeln(Format('Description               %s',[VarStrNull(SWbemPropertySet.Item('Description', 0).Get_Value)]));// String
        Writeln(Format('Height                    %s',[VarStrNull(SWbemPropertySet.Item('Height', 0).Get_Value)]));// Real32
        Writeln(Format('HostingBoard              %s',[VarStrNull(SWbemPropertySet.Item('HostingBoard', 0).Get_Value)]));// Boolean
        Writeln(Format('HotSwappable              %s',[VarStrNull(SWbemPropertySet.Item('HotSwappable', 0).Get_Value)]));// Boolean
        Writeln(Format('InstallDate               %s',[VarStrNull(SWbemPropertySet.Item('InstallDate', 0).Get_Value)]));// Datetime
        Writeln(Format('Manufacturer              %s',[VarStrNull(SWbemPropertySet.Item('Manufacturer', 0).Get_Value)]));// String
        Writeln(Format('Model                     %s',[VarStrNull(SWbemPropertySet.Item('Model', 0).Get_Value)]));// String
        Writeln(Format('Name                      %s',[VarStrNull(SWbemPropertySet.Item('Name', 0).Get_Value)]));// String
        Writeln(Format('OtherIdentifyingInfo      %s',[VarStrNull(SWbemPropertySet.Item('OtherIdentifyingInfo', 0).Get_Value)]));// String
        Writeln(Format('PartNumber                %s',[VarStrNull(SWbemPropertySet.Item('PartNumber', 0).Get_Value)]));// String
        Writeln(Format('PoweredOn                 %s',[VarStrNull(SWbemPropertySet.Item('PoweredOn', 0).Get_Value)]));// Boolean
        Writeln(Format('Product                   %s',[VarStrNull(SWbemPropertySet.Item('Product', 0).Get_Value)]));// String
        Writeln(Format('Removable                 %s',[VarStrNull(SWbemPropertySet.Item('Removable', 0).Get_Value)]));// Boolean
        Writeln(Format('Replaceable               %s',[VarStrNull(SWbemPropertySet.Item('Replaceable', 0).Get_Value)]));// Boolean
        Writeln(Format('RequirementsDescription   %s',[VarStrNull(SWbemPropertySet.Item('RequirementsDescription', 0).Get_Value)]));// String
        Writeln(Format('RequiresDaughterBoard     %s',[VarStrNull(SWbemPropertySet.Item('RequiresDaughterBoard', 0).Get_Value)]));// Boolean
        Writeln(Format('SerialNumber              %s',[VarStrNull(SWbemPropertySet.Item('SerialNumber', 0).Get_Value)]));// String
        Writeln(Format('SKU                       %s',[VarStrNull(SWbemPropertySet.Item('SKU', 0).Get_Value)]));// String
        Writeln(Format('SlotLayout                %s',[VarStrNull(SWbemPropertySet.Item('SlotLayout', 0).Get_Value)]));// String
        Writeln(Format('SpecialRequirements       %s',[VarStrNull(SWbemPropertySet.Item('SpecialRequirements', 0).Get_Value)]));// Boolean
        Writeln(Format('Status                    %s',[VarStrNull(SWbemPropertySet.Item('Status', 0).Get_Value)]));// String
        Writeln(Format('Tag                       %s',[VarStrNull(SWbemPropertySet.Item('Tag', 0).Get_Value)]));// String
        Writeln(Format('Version                   %s',[VarStrNull(SWbemPropertySet.Item('Version', 0).Get_Value)]));// String
        Writeln(Format('Weight                    %s',[VarStrNull(SWbemPropertySet.Item('Weight', 0).Get_Value)]));// Real32
        Writeln(Format('Width                     %s',[VarStrNull(SWbemPropertySet.Item('Width', 0).Get_Value)]));// Real32
        Writeln('');
        TempObj:=Unassigned;
    end;

end;

begin
 try
    CoInitialize(nil);
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

Using Delphi and Late-Binding

Using the CreateOleObject function

You can access the WMI using the CreateOleObject function from delphi passing the WbemScripting.SWbemLocator class name

  var
     FSWbemLocator : OLEVariant;
  begin
    FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
    //
  end;

And then to run a WQL sentence

const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  //create an instance to the WMI Scripting SWbemLocator
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //connect to the server
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  //execute the WQL sentence
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',wbemFlagForwardOnly);
  //get the enumerator
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  //traverse the data
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Caption    %s',[FWbemObject.Caption]));// String
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

Using a Moniker

Another way to access the WMI is using a Moniker, to do this we need to use the IBindCtx and IMoniker interfaces.

Check this sample to create a wmi instance using these interfaces.

  function GetWMIObject(const objectName: String): IDispatch;
  var
    chEaten: Integer;
    BindCtx: IBindCtx;//for access to a bind context
    Moniker: IMoniker;//Enables you to use a moniker object
  begin
    OleCheck(CreateBindCtx(0, bindCtx));
    OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));//Converts a string into a moniker that identifies the object named by the string
    OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));//Binds to the specified object
  end;

//and call in this way
GetWMIObject('winmgmts:\\localhost\root\CIMV2');

Now we can run the query an get the results

See the snippet, is very similar to the used when we import the WbemScripting_TLB unit. but the main difference is which all the variables are declarated as Variants (OLEVariant) because we don’t have available the types, enumeratios and constants of the WbemScripting_TLB unit.

var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;

3) finally to access a particular property we can get the value directly using his name.

 while oEnum.Next(1, colItem, iValue) = 0 do
 begin
     Writeln('Serial Number : '+colItem.SerialNumber);
     colItem:=Unassigned; //avoid memory leaks caused by the oEnum.Next function
 end;

Advantages of Late Binding

1. Is a very flexible solution to access the WMI, and the code to manage the wmi objects is very close to vbscript, which is good because there are thousands of examples of vbscript on the Internet to access the WMI Data.

2. Another very important advantage is that code which uses late binding is more certain to be version-independent, because when you create a WMIObject using winmgmts you are not referencing any particular version of the WMI.

3. The final exe executable is small because you don’t import any wrapper.

Drawbacks

1. You don’t have access to the wmi types, constants and enumerations from the ide, because the code is interpreted in run-time.

2. Hard to debug syntax errors,because the compiler don’t know about the WMI types
so you can write something like this (which is wrong) and the code will be compiled anyway.

 colItems.Properties_('Prop').Qualifiers_;

the right version must be.

 colItems.Properties_.Item('Prop').Qualifiers_;

Check the code using Late Binding to access the WMI from delphi (Valid for versions 5 to XE)

program WmiDelphiWin32_LateBinding;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;//introduced in delphi 6, if you use a older version of delphi you just remove this

function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: Integer;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

//The Win32_BaseBoard class represents a base board (also known as a motherboard
//or system board).

procedure  GetWin32_BaseBoardInfo;
var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
    Writeln(Format('Caption                  %s',[VarStrNull(colItem.Caption)]));// String
    Writeln(Format('ConfigOptions            %s',[VarStrNull(colItem.ConfigOptions)]));// String
    Writeln(Format('CreationClassName        %s',[VarStrNull(colItem.CreationClassName)]));// String
    Writeln(Format('Depth                    %s',[VarStrNull(colItem.Depth)]));// Real32
    Writeln(Format('Description              %s',[VarStrNull(colItem.Description)]));// String
    Writeln(Format('Height                   %s',[VarStrNull(colItem.Height)]));// Real32
    Writeln(Format('HostingBoard             %s',[VarStrNull(colItem.HostingBoard)]));// Boolean
    Writeln(Format('HotSwappable             %s',[VarStrNull(colItem.HotSwappable)]));// Boolean
    Writeln(Format('InstallDate              %s',[VarStrNull(colItem.InstallDate)]));// Datetime
    Writeln(Format('Manufacturer             %s',[VarStrNull(colItem.Manufacturer)]));// String
    Writeln(Format('Model                    %s',[VarStrNull(colItem.Model)]));// String
    Writeln(Format('Name                     %s',[VarStrNull(colItem.Name)]));// String
    Writeln(Format('OtherIdentifyingInfo     %s',[VarStrNull(colItem.OtherIdentifyingInfo)]));// String
    Writeln(Format('PartNumber               %s',[VarStrNull(colItem.PartNumber)]));// String
    Writeln(Format('PoweredOn                %s',[VarStrNull(colItem.PoweredOn)]));// Boolean
    Writeln(Format('Product                  %s',[VarStrNull(colItem.Product)]));// String
    Writeln(Format('Removable                %s',[VarStrNull(colItem.Removable)]));// Boolean
    Writeln(Format('Replaceable              %s',[VarStrNull(colItem.Replaceable)]));// Boolean
    Writeln(Format('RequirementsDescription  %s',[VarStrNull(colItem.RequirementsDescription)]));// String
    Writeln(Format('RequiresDaughterBoard    %s',[VarStrNull(colItem.RequiresDaughterBoard)]));// Boolean
    Writeln(Format('SerialNumber             %s',[VarStrNull(colItem.SerialNumber)]));// String
    Writeln(Format('SKU                      %s',[VarStrNull(colItem.SKU)]));// String
    Writeln(Format('SlotLayout               %s',[VarStrNull(colItem.SlotLayout)]));// String
    Writeln(Format('SpecialRequirements      %s',[VarStrNull(colItem.SpecialRequirements)]));// Boolean
    Writeln(Format('Status                   %s',[VarStrNull(colItem.Status)]));// String
    Writeln(Format('Tag                      %s',[VarStrNull(colItem.Tag)]));// String
    Writeln(Format('Version                  %s',[VarStrNull(colItem.Version)]));// String
    Writeln(Format('Weight                   %s',[VarStrNull(colItem.Weight)]));// Real32
    Writeln(Format('Width                    %s',[VarStrNull(colItem.Width)]));// Real32
    Writeln('');
    colItem:=Unassigned;
  end;
end;

begin
 try
    CoInitialize(nil);
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
    CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

Using Delphi .Net (Valid for versions 2005, 2006, 2007)

1) before to use the .net wmi objects you add the System.Management reference to your project.

2) Now we need make the connection to the WMi service, to do this you must use the System.Management namespace and the ManagementObjectSearcher class wich have several constructor to facilitate the connection with the wmi, this class is very versatile and let establish the connection and make a WQL query in a single step.

Check the constructors availables to this class

Public method ManagementObjectSearcher() 	//Initializes a new instance of the ManagementObjectSearcher class. After some properties on this object are set, the object can be used to invoke a query for management information. This is the default constructor.

 

Public method ManagementObjectSearcher(ObjectQuery) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query for management information.

 

Public method ManagementObjectSearcher(String) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query for management information.

 

Public method ManagementObjectSearcher(ManagementScope, ObjectQuery) //Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query in the specified scope.

 

Public method ManagementObjectSearcher(String, String) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query in the specified scope.

 

Public method ManagementObjectSearcher(ManagementScope, ObjectQuery, EnumerationOptions) 	//Initializes a new instance of the

 

Public method ManagementObjectSearcher class //to be used to invoke the specified query in the specified scope, with the specified options.

 

Public method ManagementObjectSearcher(String, String, EnumerationOptions) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query, in the specified scope, and with the specified options.

so to establishing the connection and make the query to the WMI you must write a code like this

Searcher  :=ManagementObjectSearcher.Create('root\cimv2','SELECT * FROM Win32_BaseBoard');

3) retrieve the data using the ManagementObjectCollection object.

4) get a enumerator to iterate over the data returned using a ManagementObjectEnumerator

    Collection:=Searcher.Get();//Get the data
    iter:=Collection.GetEnumerator;//create  a enumerator
    while(iter.MoveNext()) do//iterate over the enumerator

5) and finally to access to the value of an particular property we must use a ManagementObject object in this way

      WmiObject:=ManagementObject(iter.Current); //get the current element
      SerialNumber:=WmiObject['SerialNumber'];

This is the full source code to access the wmi from Delphi .Net from a console application

program WmiDelphi.Net;
{$APPTYPE CONSOLE}
uses
  System.Management,
  SysUtils;

procedure  GetWin32_BaseBoardInfo;
var
Searcher   : ManagementObjectSearcher ;
Collection : ManagementObjectCollection;
iter       : ManagementObjectCollection.ManagementObjectEnumerator;
WmiObject  : ManagementObject;
begin
  try
    Searcher  :=ManagementObjectSearcher.Create('SELECT * FROM Win32_BaseBoard'); //make the WMi query
    Collection:=Searcher.Get();//Get the data
    iter:=Collection.GetEnumerator;//create  a enumerator
    while(iter.MoveNext()) do//iterate over the enumerator
    begin
      WmiObject:=ManagementObject(iter.Current); //get the current element
         Writeln(Format('Caption                 %s',[WmiObject['Caption']]));
         Writeln(Format('ConfigOptions           %s',[WmiObject['ConfigOptions']]));
         Writeln(Format('CreationClassName       %s',[WmiObject['CreationClassName']]));
         Writeln(Format('Depth                   %s',[WmiObject['Depth']]));
         Writeln(Format('Description             %s',[WmiObject['Description']]));
         Writeln(Format('Height                  %s',[WmiObject['Height']]));
         Writeln(Format('HostingBoard            %s',[WmiObject['HostingBoard']]));
         Writeln(Format('HotSwappable            %s',[WmiObject['HotSwappable']]));
         Writeln(Format('InstallDate             %s',[WmiObject['InstallDate']]));
         Writeln(Format('Manufacturer            %s',[WmiObject['Manufacturer']]));
         Writeln(Format('Model                   %s',[WmiObject['Model']]));
         Writeln(Format('Name                    %s',[WmiObject['Name']]));
         Writeln(Format('OtherIdentifyingInfo    %s',[WmiObject['OtherIdentifyingInfo']]));
         Writeln(Format('PartNumber              %s',[WmiObject['PartNumber']]));
         Writeln(Format('PoweredOn               %s',[WmiObject['PoweredOn']]));
         Writeln(Format('Product                 %s',[WmiObject['Product']]));
         Writeln(Format('Removable               %s',[WmiObject['Removable']]));
         Writeln(Format('Replaceable             %s',[WmiObject['Replaceable']]));
         Writeln(Format('RequirementsDescription %s',[WmiObject['RequirementsDescription']]));
         Writeln(Format('RequiresDaughterBoard   %s',[WmiObject['RequiresDaughterBoard']]));
         Writeln(Format('SerialNumber            %s',[WmiObject['SerialNumber']]));
         Writeln(Format('SKU                     %s',[WmiObject['SKU']]));
         Writeln(Format('SlotLayout              %s',[WmiObject['SlotLayout']]));
         Writeln(Format('SpecialRequirements     %s',[WmiObject['SpecialRequirements']]));
         Writeln(Format('Status                  %s',[WmiObject['Status']]));
         Writeln(Format('Tag                     %s',[WmiObject['Tag']]));
         Writeln(Format('Version                 %s',[WmiObject['Version']]));
         Writeln(Format('Weight                  %s',[WmiObject['Weight']]));
         Writeln(Format('Width                   %s',[WmiObject['Width']]));
    end;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end;

begin
  try
    GetWin32_BaseBoardInfo;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

Using Delphi Prism

By far using .Net and delphi prism must be the easy way to access the WMI, because you don’t need to implement enumerators or helper functions to avoid nulls. therefore exists many resources of the WMI and .Net in the MSDN site.

1) to access the WMI from .Net you add the System.Management namespace which give you full access to the WMI.

2) Now using the ManagementObjectSearcher class in a single step you can establish a connection to the wmi service and make a WQL query.

Searcher := new ManagementObjectSearcher('root\cimv2','select * from Win32_BaseBoard');

3) for iterate over the WMI data you can use the great features of dephi-prism, how create a local variable in a for loop

for WmiObject : ManagementObject  in searcher.Get() do //in this single line you are creating a WmiObject to get access to the properties,and searcher.Get() return a enumerator that iterates through the ManagementObjectCollection.

Finally the Source code of a Delphi Prism console application to access the WMI.

namespace WmiDelphiPrism;

interface
uses
System,
System.Management,
System.Text;

type
    ConsoleApp = class
    private
        class method GetWin32_BaseBoardInfo;
    public
        class method Main;
    end;

implementation

class method ConsoleApp.Main;
begin
 try
    GetWin32_BaseBoardInfo;
    Console.Read();
 except on E: Exception do
  Console.WriteLine(E.ToString()+' Trace '+E.StackTrace );
 end;
end;

class method ConsoleApp.GetWin32_BaseBoardInfo;
var
Searcher : ManagementObjectSearcher;
begin
     Searcher := new ManagementObjectSearcher('root\cimv2','select * from Win32_BaseBoard');
        for WmiObject : ManagementObject  in searcher.Get() do
        begin
             Console.WriteLine('{0,-35} {1,-40}','Caption',WmiObject['Caption']);
             Console.WriteLine('{0,-35} {1,-40}','ConfigOptions',WmiObject['ConfigOptions']);
             Console.WriteLine('{0,-35} {1,-40}','CreationClassName',WmiObject['CreationClassName']);
             Console.WriteLine('{0,-35} {1,-40}','Depth',WmiObject['Depth']);
             Console.WriteLine('{0,-35} {1,-40}','Description',WmiObject['Description']);
             Console.WriteLine('{0,-35} {1,-40}','Height',WmiObject['Height']);
             Console.WriteLine('{0,-35} {1,-40}','HostingBoard',WmiObject['HostingBoard']);
             Console.WriteLine('{0,-35} {1,-40}','HotSwappable',WmiObject['HotSwappable']);
             Console.WriteLine('{0,-35} {1,-40}','InstallDate',WmiObject['InstallDate']);
             Console.WriteLine('{0,-35} {1,-40}','Manufacturer',WmiObject['Manufacturer']);
             Console.WriteLine('{0,-35} {1,-40}','Model',WmiObject['Model']);
             Console.WriteLine('{0,-35} {1,-40}','Name',WmiObject['Name']);
             Console.WriteLine('{0,-35} {1,-40}','OtherIdentifyingInfo',WmiObject['OtherIdentifyingInfo']);
             Console.WriteLine('{0,-35} {1,-40}','PartNumber',WmiObject['PartNumber']);
             Console.WriteLine('{0,-35} {1,-40}','PoweredOn',WmiObject['PoweredOn']);
             Console.WriteLine('{0,-35} {1,-40}','Product',WmiObject['Product']);
             Console.WriteLine('{0,-35} {1,-40}','Removable',WmiObject['Removable']);
             Console.WriteLine('{0,-35} {1,-40}','Replaceable',WmiObject['Replaceable']);
             Console.WriteLine('{0,-35} {1,-40}','RequirementsDescription',WmiObject['RequirementsDescription']);
             Console.WriteLine('{0,-35} {1,-40}','RequiresDaughterBoard',WmiObject['RequiresDaughterBoard']);
             Console.WriteLine('{0,-35} {1,-40}','SerialNumber',WmiObject['SerialNumber']);
             Console.WriteLine('{0,-35} {1,-40}','SKU',WmiObject['SKU']);
             Console.WriteLine('{0,-35} {1,-40}','SlotLayout',WmiObject['SlotLayout']);
             Console.WriteLine('{0,-35} {1,-40}','SpecialRequirements',WmiObject['SpecialRequirements']);
             Console.WriteLine('{0,-35} {1,-40}','Status',WmiObject['Status']);
             Console.WriteLine('{0,-35} {1,-40}','Tag',WmiObject['Tag']);
             Console.WriteLine('{0,-35} {1,-40}','Version',WmiObject['Version']);
             Console.WriteLine('{0,-35} {1,-40}','Weight',WmiObject['Weight']);
             Console.WriteLine('{0,-35} {1,-40}','Width',WmiObject['Width']);
        end;
end;
end.

Using Lazarus

The code necessary to access the WMI from Free-pascal using lazarus is very similar to the used in the Delphi win32 Late-binding., so i will show the differences only because the general idea is the same.

1) you must add the Windows unit to the uses clause because this unit contan the PULONG type which is required in some functions.

2) you don’t need call CoInitialize and CoUninitialize functions because both are initializated by the comobj unit

3) the helper function GetWMIObject must be modified to fit with the new types of parameteres required by the MkParseDisplayName function.

which is declarated like this (see the _para3 param which is of PULONG (^cardinal) type)

 function MkParseDisplayName(_para1:IBindCtx; _para2:POLESTR; out _para3:PULONG; out _para4:IMoniker):HRESULT;stdcall; external  'ole32.dll' name 'MkParseDisplayName';

and the modified helper function now look like this.

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: PULONG;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

4) the function IEnumVARIANT.Next requires a Variant param

Function  Next(celt: ULONG; OUT rgVar: VARIANT;  pCeltFetched: pULONG=nil):HResult;StdCall;

so you must use it in this way

var
colItem : Variant;
//
//
//
while oEnum.Next(1, colItem, nil) = 0 do

5) finally if you wanna use the format function, you must aware which has some problems with variant values. so you must rewrite the code to show the info in this way.

    SDummy:=VarStrNull(colItem.Caption); //SDummy is a string
    Writeln(Format('Caption                  %s',[SDummy]));

Lazarus source code of console aplication to access the WMI

program WmiLazarus_LateBinding;

{$mode objfpc}

uses
   SysUtils,
   Variants,
   comobj,//required for the OleCheck and CoInitialize functions
   ActiveX,
   Windows;//required for the PULONG type

function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: PULONG;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

//The Win32_BaseBoard class represents a base board (also known as a motherboard
//or system board).

procedure  GetWin32_BaseBoardInfo;
var
  objWMIService : OleVariant;
  colItems      : OleVariant;
  colItem       : Variant;
  oEnum         : IEnumvariant;
  SDummy        : string;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, nil) = 0 do
  begin
    SDummy:=VarStrNull(colItem.Caption);
    Writeln(Format('Caption                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.ConfigOptions);
    Writeln(Format('ConfigOptions            %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.CreationClassName);
    Writeln(Format('CreationClassName        %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Depth);
    Writeln(Format('Depth                    %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.Description);
    Writeln(Format('Description              %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Height);
    Writeln(Format('Height                   %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.HostingBoard);
    Writeln(Format('HostingBoard             %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.HotSwappable);
    Writeln(Format('HotSwappable             %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.InstallDate);
    Writeln(Format('InstallDate              %s',[SDummy]));// Datetime
    SDummy:=VarStrNull(colItem.Manufacturer);
    Writeln(Format('Manufacturer             %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Model);
    Writeln(Format('Model                    %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Name);
    Writeln(Format('Name                     %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.OtherIdentifyingInfo);
    Writeln(Format('OtherIdentifyingInfo     %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.PartNumber);
    Writeln(Format('PartNumber               %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.PoweredOn);
    Writeln(Format('PoweredOn                %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Product);
    Writeln(Format('Product                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Removable);
    Writeln(Format('Removable                %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Replaceable);
    Writeln(Format('Replaceable              %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.RequirementsDescription);
    Writeln(Format('RequirementsDescription  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.RequiresDaughterBoard);
    Writeln(Format('RequiresDaughterBoard    %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.SerialNumber);
    Writeln(Format('SerialNumber             %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SKU);
    Writeln(Format('SKU                      %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SlotLayout);
    Writeln(Format('SlotLayout               %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SpecialRequirements);
    Writeln(Format('SpecialRequirements      %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Status);
    Writeln(Format('Status                   %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Tag);
    Writeln(Format('Tag                      %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Version);
    Writeln(Format('Version                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Weight);
    Writeln(Format('Weight                   %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.Width);
    Writeln(Format('Width                    %s',[SDummy]));// Real32
  end;

end;

begin
 try
   //CoInitialize(nil);   you don't need call this because is initializated by the comobj unit
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
    //CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

So now you don’t have excuses to don’t use the WMI for pascal code. And remember which you have the WMI Delphi Code creator to help you.;)

Author: Rodrigo

Just another Delphi guy.

13 thoughts on “Accesing the WMI from Object Pascal Code (Delphi, Oxygene, FreePascal)

  1. Very nice article, detailed and pleasant to read.

    I like the fact that you embed the code into the text. It’s definitively much more easy to follow than download an archive file. For such snippets, it’s very easy to follow.

    And I definitively enjoy your Lazarus/FPC touch.
    So nice seeing it’s so close to Delphi.

  2. Fantástico artículo y muy completo Rodrigo.

    Un saludo.

  3. Excelent article. Congratulations.

    =========

    Rodrigo, vaya peazo de artículo. Enhorabuena.

  4. This is great work! I’m most interested in using Delphi (Win32) and late-binding…but my question is: “How do I query a remote machine with late binding and WMI”?

    Or similarly, how do I execute a method on a remote machine using late binding and WMI? Is this possible?

    Thank you for all of your work!

    • you must modify the code in order to connect to a remote machine. check this sample

      var
        objSWbemLocator : OleVariant;
        objWMIService   : OleVariant;
        colItems        : OLEVariant;
        colItem         : OLEVariant;
        oEnum           : IEnumvariant;
        iValue          : LongWord;
        strComputer     : string;
        User            : string;
        password        : string;
      begin;
        strComputer     := 'pcname';
        User            := 'username';
        password        := 'password';
        objSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
        objWMIService   := objSWbemLocator.ConnectServer(strComputer, 'root\cimv2', User, password);
        colItems        := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
      
  5. Thanks.

    A great article.

    On reading the first thought that came to my mind about accessing WMI services is to go through psvHost/psvAppletHost VCL component. I personally think it is a better way to access any kind of ActiveX controls and objects under windows 32Bit without having to go through the hassle of importing them.

    Do check it. Who know it may give you more better ideas and may also motivate you to upgrade is.

    Do check it in your spare time.

    http://users.telenet.be/ws36637/#applet

  6. Pingback: A New project – Delphi (Object Pascal) WMI class code generator « The Road to Delphi – a Blog About Delphi Programming (mostly)

  7. Pingback: Delphi and WMI Events | The Road to Delphi - a Blog about programming

  8. FYI: MadExcept detects a memory leak when using StringToOleStr() without a call to SysFreeString().

  9. Hello Rodrigo (or anyone else who knows the answer :-),

    I try to use your great FPC sample code in my app, but I can’t get over one point for hours. :-(

    The ExecQuery method works with string constant only, like

    colItems := objWMIService.ExecQuery(‘SELECT * FROM Win32_BaseBoard’,’WQL’,0);

    But I’d need to use string variable, like

    sQuery := ‘SELECT * FROM Win32_BaseBoard’; // sQuery is an AnsiString
    colItems := objWMIService.ExecQuery(sQuery,’WQL’,0);

    but I get “EOleException: Invalid query” only. Both for FPC 2.4.4 and 3.0.0 (the lates one). Both Win32 and Win64 target.

    What’s wrong, please? Any advice/help appreciated…

    Thanks.

    Michal

    • Well, the variable must be WideString, not AnsiString… :-) Elementary, my dear Watson… Sorry I’ve bothered here…

Leave a comment