The Road to Delphi

Delphi – Free Pascal – Oxygene


12 Comments

Generating a “unique” hardware ID using Delphi and the WMI

The general idea to generate a Hardware ID (machine fingerprint) is collect data from the CPU, HDD, BIOS, NIC and another hardware components which have serial numbers and unique characteristics. you can use this ID to generate licenses based in this fingerprint which must be “unique” by machine.

for this simple sample i picked this set of WMI classes Win32_Processor (Get CPU info), Win32_BaseBoard (retrieve info about the motherboard), Win32_BIOS (Get BIOS Data) and Win32_OperatingSystem (Get Windows information). Of course you can choose other WMI classes, and combinations that you want.

Check the full source code of the hardware ID generator.

(Tested in Delphi 2007 and Delphi XE)

program WMIHardwareID;

{$APPTYPE CONSOLE}
{$DEFINE Use_Jwscl} //necessary to obtain a hash of the data using md2, md4, md5 or sha1

uses
  {$IFDEF Use_Jwscl}
  JwsclTypes,
  JwsclCryptProvider,
  {$ENDIF}
  Classes,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

type
   TMotherBoardInfo   = (Mb_SerialNumber,Mb_Manufacturer,Mb_Product,Mb_Model);
   TMotherBoardInfoSet= set of TMotherBoardInfo;
   TProcessorInfo     = (Pr_Description,Pr_Manufacturer,Pr_Name,Pr_ProcessorId,Pr_UniqueId);
   TProcessorInfoSet  = set of TProcessorInfo;
   TBIOSInfo          = (Bs_BIOSVersion,Bs_BuildNumber,Bs_Description,Bs_Manufacturer,Bs_Name,Bs_SerialNumber,Bs_Version);
   TBIOSInfoSet       = set of TBIOSInfo;
   TOSInfo            = (Os_BuildNumber,Os_BuildType,Os_Manufacturer,Os_Name,Os_SerialNumber,Os_Version);
   TOSInfoSet         = set of TOSInfo;

const //properties names to get the data
   MotherBoardInfoArr: array[TMotherBoardInfo] of AnsiString =
                        ('SerialNumber','Manufacturer','Product','Model');

   OsInfoArr         : array[TOSInfo] of AnsiString =
                        ('BuildNumber','BuildType','Manufacturer','Name','SerialNumber','Version');

   BiosInfoArr       : array[TBIOSInfo] of AnsiString =
                        ('BIOSVersion','BuildNumber','Description','Manufacturer','Name','SerialNumber','Version');

   ProcessorInfoArr  : array[TProcessorInfo] of AnsiString =
                        ('Description','Manufacturer','Name','ProcessorId','UniqueId');

type
   THardwareId  = class
   private
    FOSInfo         : TOSInfoSet;
    FBIOSInfo       : TBIOSInfoSet;
    FProcessorInfo  : TProcessorInfoSet;
    FMotherBoardInfo: TMotherBoardInfoSet;
    FBuffer         : AnsiString;
    function GetHardwareIdHex: AnsiString;
  {$IFDEF Use_Jwscl}
    function GetHashString(Algorithm: TJwHashAlgorithm; Buffer : Pointer;Size:Integer) : AnsiString;
    function GetHardwareIdMd5: AnsiString;
    function GetHardwareIdMd2: AnsiString;
    function GetHardwareIdMd4: AnsiString;
    function GetHardwareIdSHA: AnsiString;
  {$ENDIF}
   public
     //Set the properties to  be used in the generation of the hardware id
    property  MotherBoardInfo : TMotherBoardInfoSet read FMotherBoardInfo write FMotherBoardInfo;
    property  ProcessorInfo : TProcessorInfoSet read FProcessorInfo write FProcessorInfo;
    property  BIOSInfo: TBIOSInfoSet read FBIOSInfo write FBIOSInfo;
    property  OSInfo  : TOSInfoSet read FOSInfo write FOSInfo;
    property  Buffer : AnsiString read FBuffer; //return the content of the data collected in the system
    property  HardwareIdHex : AnsiString read GetHardwareIdHex; //get a hexadecimal represntation of the data collected
  {$IFDEF Use_Jwscl}
    property  HardwareIdMd2  : AnsiString read GetHardwareIdMd2; //get a Md2 hash of the data collected
    property  HardwareIdMd4  : AnsiString read GetHardwareIdMd4; //get a Md4 hash of the data collected
    property  HardwareIdMd5  : AnsiString read GetHardwareIdMd5; //get a Md5 hash of the data collected
    property  HardwareIdSHA  : AnsiString read GetHardwareIdSHA; //get a SHA1 hash of the data collected
  {$ENDIF}
    procedure GenerateHardwareId; //calculate the hardware id
    constructor  Create(Generate:Boolean=True); overload;
    Destructor  Destroy; override;
   end;

function VarArrayToStr(const vArray: variant): AnsiString;

  function _VarToStr(const V: variant): AnsiString;
  var
  Vt: integer;
  begin
   Vt := VarType(V);
      case Vt of
        varSmallint,
        varInteger  : Result := AnsiString(IntToStr(integer(V)));
        varSingle,
        varDouble,
        varCurrency : Result := AnsiString(FloatToStr(Double(V)));
        varDate     : Result := AnsiString(VarToStr(V));
        varOleStr   : Result := AnsiString(WideString(V));
        varBoolean  : Result := AnsiString(VarToStr(V));
        varVariant  : Result := AnsiString(VarToStr(Variant(V)));
        varByte     : Result := AnsiChar(byte(V));
        varString   : Result := AnsiString(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):AnsiString; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=AnsiString(VarToStr(V));
  end;
end;

{ THardwareId }

constructor THardwareId.Create(Generate:Boolean=True);
begin
   inherited Create;
   CoInitialize(nil);
   FBuffer          :='';
   //Set the propeties to be used in the hardware id generation
   FMotherBoardInfo :=[Mb_SerialNumber,Mb_Manufacturer,Mb_Product,Mb_Model];
   FOSInfo          :=[Os_BuildNumber,Os_BuildType,Os_Manufacturer,Os_Name,Os_SerialNumber,Os_Version];
   FBIOSInfo        :=[Bs_BIOSVersion,Bs_BuildNumber,Bs_Description,Bs_Manufacturer,Bs_Name,Bs_SerialNumber,Bs_Version];
   FProcessorInfo   :=[];//including the processor info is expensive [Pr_Description,Pr_Manufacturer,Pr_Name,Pr_ProcessorId,Pr_UniqueId];
   if Generate then
    GenerateHardwareId;
end;

destructor THardwareId.Destroy;
begin
  CoUninitialize;
  inherited;
end;

//Main function which collect the system data.
procedure THardwareId.GenerateHardwareId;
var
  objSWbemLocator : OLEVariant;
  objWMIService   : OLEVariant;
  objWbemObjectSet: OLEVariant;
  oWmiObject      : OLEVariant;
  oEnum           : IEnumvariant;
  iValue          : LongWord;
  SDummy          : AnsiString;
  Mb              : TMotherBoardInfo;
  Os              : TOSInfo;
  Bs              : TBIOSInfo;
  Pr              : TProcessorInfo;
begin;
  objSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService   := objSWbemLocator.ConnectServer('localhost','root\cimv2', '','');

  if FMotherBoardInfo<>[] then //MotherBoard info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Mb := Low(TMotherBoardInfo) to High(TMotherBoardInfo) do
       if Mb in FMotherBoardInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(MotherBoardInfoArr[Mb]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;

  if FOSInfo<>[] then//Windows info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_OperatingSystem','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Os := Low(TOSInfo) to High(TOSInfo) do
       if Os in FOSInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(OsInfoArr[Os]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;

  if FBIOSInfo<>[] then//BIOS info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_BIOS','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Bs := Low(TBIOSInfo) to High(TBIOSInfo) do
       if Bs in FBIOSInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(BiosInfoArr[Bs]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;

  if FProcessorInfo<>[] then//CPU info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_Processor','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Pr := Low(TProcessorInfo) to High(TProcessorInfo) do
       if Pr in FProcessorInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(ProcessorInfoArr[Pr]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;

end;

function THardwareId.GetHardwareIdHex: AnsiString;
begin
    SetLength(Result,Length(FBuffer)*2);
    BinToHex(PAnsiChar(FBuffer),PAnsiChar(Result),Length(FBuffer));
end;

{$IFDEF Use_Jwscl}
function THardwareId.GetHashString(Algorithm: TJwHashAlgorithm; Buffer : Pointer;Size:Integer) : AnsiString;
var
  Hash: TJwHash;
  HashSize: Cardinal;
  HashData: Pointer;
begin
  Hash := TJwHash.Create(Algorithm);
  try
    Hash.HashData(Buffer,Size);
    HashData := Hash.RetrieveHash(HashSize);
    try
        SetLength(Result,HashSize*2);
        BinToHex(PAnsiChar(HashData),PAnsiChar(Result),HashSize);
    finally
      TJwHash.FreeBuffer(HashData);
    end;
  finally
    Hash.Free;
  end;
end;

function THardwareId.GetHardwareIdMd2: AnsiString;
begin
   Result:=GetHashString(haMD2,@FBuffer[1],Length(FBuffer));
end;

function THardwareId.GetHardwareIdMd4: AnsiString;
begin
   Result:=GetHashString(haMD4,@FBuffer[1],Length(FBuffer));
end;

function THardwareId.GetHardwareIdMd5: AnsiString;
begin
   Result:=GetHashString(haMD5,@FBuffer[1],Length(FBuffer));
end;

function THardwareId.GetHardwareIdSHA: AnsiString;
begin
   Result:=GetHashString(haSHA,@FBuffer[1],Length(FBuffer));
end;

{$ENDIF}

//testing the THardwareId object
var
  HWID : THardwareId;
  dt   : TDateTime;
begin
 try
    HWID:=THardwareId.Create(False);
    try
       dt := Now;
       HWID.GenerateHardwareId;
       dt := now - dt;
       Writeln(Format('Hardware Id Generated in %s',[FormatDateTime('hh:mm:nn.zzz',dt)]));
       Writeln(Format('%s %s',['Buffer ',HWID.Buffer]));
       Writeln('');
       Writeln(Format('%s %s',['Hex  ',HWID.HardwareIdHex]));
      {$IFDEF Use_Jwscl}
       Writeln(Format('%s %s',['Md2  ',HWID.HardwareIdMd2]));
       Writeln(Format('%s %s',['Md4  ',HWID.HardwareIdMd4]));
       Writeln(Format('%s %s',['Md5  ',HWID.HardwareIdMd5]));
       Writeln(Format('%s %s',['SHA1 ',HWID.HardwareIdSHA]));
      {$ENDIF}
      Readln;
    finally
     HWID.Free;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.


13 Comments

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

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.;)


4 Comments

Be careful when you import the Microsoft WMIScripting Library

Commonly when we need use the WMI, we import the Microsoft WMIScripting Library from delphi using the Import Component Wizard, but depending on the version of windows that we use the results can be different, although the version showed by the wizard is the same.

Check this sample.

if you uses Windows 7 and Delphi 2007 to import the Microsoft WMIScripting Library as is shown in this picture.

Delphi will generate a unit called WbemScripting_TLB wich is based in the C:\Windows\system32\wbem\wbemdisp.TLB file.

Now if you look the ISWbemObjectSet interface the declaration will look like this

// *********************************************************************//
// Interface: ISWbemObjectSet
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {76A6415F-CB41-11D1-8B02-00600806D9B6}
// *********************************************************************//
  ISWbemObjectSet = interface(IDispatch)
    ['{76A6415F-CB41-11D1-8B02-00600806D9B6}']
    function Get__NewEnum: IUnknown; safecall;
    function Item(const strObjectPath: WideString; iFlags: Integer): ISWbemObject; safecall;
    function Get_Count: Integer; safecall;
    function Get_Security_: ISWbemSecurity; safecall;
    function ItemIndex(lIndex: Integer): ISWbemObject; safecall;
    property _NewEnum: IUnknown read Get__NewEnum;
    property Count: Integer read Get_Count;
    property Security_: ISWbemSecurity read Get_Security_;
  end;

Doing the same operation from Windows XP the declaration for the ISWbemObjectSet interface is this

// *********************************************************************//
// Interface: ISWbemObjectSet
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {76A6415F-CB41-11D1-8B02-00600806D9B6}
// *********************************************************************//
  ISWbemObjectSet = interface(IDispatch)
    ['{76A6415F-CB41-11D1-8B02-00600806D9B6}']
    function Get__NewEnum: IUnknown; safecall;
    function Item(const strObjectPath: WideString; iFlags: Integer): ISWbemObject; safecall;
    function Get_Count: Integer; safecall;
    function Get_Security_: ISWbemSecurity; safecall;
    property _NewEnum: IUnknown read Get__NewEnum;
    property Count: Integer read Get_Count;
    property Security_: ISWbemSecurity read Get_Security_;
  end;

As you can see the difference is the ItemIndex function, which according to Microsoft is available since Windows Vista.

function ItemIndex(lIndex: Integer): ISWbemObject; safecall;

this function returns a SWbemObject associated with the specified index into the collection returned by the SWbemObjectSet

The problem is which you have a simple code like this (compiled under Windows Vista, 2008 or 7)

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

procedure ShowMotherBoardInfo;
var
  WMIServices    : ISWbemServices;
  SWbemObjectSet : ISWbemObjectSet;
  SObject        : Variant;
  I              : Integer;
begin
  WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  SWbemObjectSet := WMIServices.ExecQuery('Select * FROM Win32_BaseBoard', 'WQL', 0, nil);
  for I := 0 to SWbemObjectSet.Count - 1 do
  begin
    SObject := SWbemObjectSet.ItemIndex(I);
    Writeln('SerialNumber '+SObject.SerialNumber);
  end;
end;

and you deploy your exe in a Windows XP system, your application will raise an awful exception like this

EAccessViolation:Access violation at address 00570053. Write of address 5A732CB4

because that function does not exist in the WMI deployed with Windows XP.

 

if  look deeper in the wbemdisp.TLB file you can see the differences in the versions

Windows XP version information for the wbemdisp.TLB file

Length Of Struc: 039Ch
Length Of Value: 0034h
Type Of Struc:   0000h
Info:            VS_VERSION_INFO
Signature:       FEEF04BDh
Struc Version:   1.0
File Version:    5.1.2600.0
Product Version: 5.1.2600.0
File Flags Mask: 0.63
File Flags:
File OS:         NT (WINDOWS32)
File Type:       DLL
File SubType:    UNKNOWN
File Date:       00:00:00  00/00/0000

     Struc has Child(ren). Size: 832 bytes.

Child Type:         StringFileInfo
Language/Code Page: 1033/1200
CompanyName:        Microsoft Corporation
FileDescription:    Typelib for WMI Scripting Interface
FileVersion:        5.1.2600.0 (xpclient.010817-1148)
InternalName:       wbemdisp
LegalCopyright:     © Microsoft Corporation. All rights reserved.
OriginalFilename:   wbemdisp.tlb
ProductName:        Microsoft® Windows® Operating System
ProductVersion:     5.1.2600.0

Child Type:         VarFileInfo
Translation:        1033/1200

Windows 7 version information for the wbemdisp.TLB file

Length Of Struc: 03ACh
Length Of Value: 0034h
Type Of Struc:   0000h
Info:            VS_VERSION_INFO
Signature:       FEEF04BDh
Struc Version:   1.0
File Version:    6.1.7600.16385
Product Version: 6.1.7600.16385
File Flags Mask: 0.63
File Flags:
File OS:         NT (WINDOWS32)
File Type:       DLL
File SubType:    UNKNOWN
File Date:       00:00:00  00/00/0000

     Struc has Child(ren). Size: 848 bytes.

Child Type:         StringFileInfo
Language/Code Page: 1033/1200
CompanyName:        Microsoft Corporation
FileDescription:    Typelib for WMI Scripting Interface
FileVersion:        6.1.7600.16385 (win7_rtm.090713-1255)
InternalName:       wbemdisp
LegalCopyright:     © Microsoft Corporation. All rights reserved.
OriginalFilename:   wbemdisp.tlb
ProductName:        Microsoft® Windows® Operating System
ProductVersion:     6.1.7600.16385

Child Type:         VarFileInfo
Translation:        1033/1200

and now the dump of the wbemdisp.TLB file

Windows XP Version

//A collection of Classes or Instances
Dispatch ISWbemObjectSet;
GUID = {76A6415F-CB41-11D1-8B02-00600806D9B6};
  function QueryInterface(riid: ^GUID; out ppvObj: ^^VOID); stdcall;
  function AddRef: UI4; stdcall;
  function Release: UI4; stdcall;
  function GetTypeInfoCount(out pctinfo: ^UINT); stdcall;
  function GetTypeInfo(itinfo: UINT; lcid: UI4; out pptinfo: ^^VOID); stdcall;
  function GetIDsOfNames(riid: ^GUID; rgszNames: ^^I1; cNames: UINT; lcid: UI4; out rgdispid: ^I4); stdcall;
  function Invoke(dispidMember: I4; riid: ^GUID; lcid: UI4; wFlags: UI2; pdispparams: ^DISPPARAMS; out pvarResult: ^Variant; out pexcepinfo: ^EXCEPINFO; out puArgErr: ^UINT); stdcall;
  property-get _NewEnum: IUnknown; stdcall;
  //Get an Object with a specific path from this collection
  function Item(strObjectPath: BSTR; out iFlags: I4): ^ISWbemObject; stdcall;
  //The number of items in this collection
  property-get Count: I4; stdcall;
  //The Security Configurator for this Object
  property-get Security_: ^ISWbemSecurity; stdcall;

Windows 7 Version

//A collection of Classes or Instances
Dispatch ISWbemObjectSet;
GUID = {76A6415F-CB41-11D1-8B02-00600806D9B6};
  function QueryInterface(riid: ^GUID; out ppvObj: ^^VOID); stdcall;
  function AddRef: UI4; stdcall;
  function Release: UI4; stdcall;
  function GetTypeInfoCount(out pctinfo: ^UINT); stdcall;
  function GetTypeInfo(itinfo: UINT; lcid: UI4; out pptinfo: ^^VOID); stdcall;
  function GetIDsOfNames(riid: ^GUID; rgszNames: ^^I1; cNames: UINT; lcid: UI4; out rgdispid: ^I4); stdcall;
  function Invoke(dispidMember: I4; riid: ^GUID; lcid: UI4; wFlags: UI2; pdispparams: ^DISPPARAMS; out pvarResult: ^Variant; out pexcepinfo: ^EXCEPINFO; out puArgErr: ^UINT); stdcall;
  property-get _NewEnum: IUnknown; stdcall;
  //Get an Object with a specific path from this collection
  function Item(strObjectPath: BSTR; out iFlags: I4): ^ISWbemObject; stdcall;
  //The number of items in this collection
  property-get Count: I4; stdcall;
  //The Security Configurator for this Object
  property-get Security_: ^ISWbemSecurity; stdcall;
  //Get an Object with a specific index from this collection
  function ItemIndex(lIndex: I4): ^ISWbemObject; stdcall;

returning to the original sample code , you can rewrite the procedure like this to avoid the problem

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

procedure ShowMotherBoardSerialNumber;
var
  WMIServices     : ISWbemServices;
  SWbemObjectSet  : ISWbemObjectSet;
  SObject         : ISWbemObject;
  Enum            : IEnumVariant;
  TempObj         : OleVariant;
  Value           : Cardinal;
  SWbemPropertySet: ISWbemPropertySet;
begin
  WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  SWbemObjectSet := WMIServices.ExecQuery('Select * FROM Win32_BaseBoard', 'WQL', 0, nil);
  Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;
  while (Enum.Next(1, TempObj, Value) = S_OK) do
  begin
    SObject     := IUnknown(tempObj) as ISWBemObject;
    SWbemPropertySet := SObject.Properties_;
    Writeln('SerialNumber '+SWbemPropertySet.Item('SerialNumber', 0).Get_Value);
  end;
end;

So the lesson is be careful when you import the Microsoft WMIScripting Library and check your code in another Windows versions, before your deploy the final version of your application.


10 Comments

WMI Delphi Code Creator – New Features

Update :  to get last version  check the new page of this project

Many new features was added to the application

The tool allows you compile and run the generated code directly without leaving the application

check the screen to select the installed Delphi compiler to use.

and the compiler result output

Added support for call WMI methods

Check the generated code for the Win32_Process wmi class and the Create method.

//------------------------------------------------------------------------------
//     This code was generated by the Wmi Delphi Code Creator https://theroadtodelphi.wordpress.com
//     Version: 1.0.0.11
//
//
//
//     LIABILITY DISCLAIMER
//     THIS GENERATED CODE IS DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED.
//     YOU USE IT AT YOUR OWN RISK. THE AUTHOR NOT WILL BE LIABLE FOR DATA LOSS,
//     DAMAGES AND LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS CODE.
//
//
//------------------------------------------------------------------------------
program GetWMI_Info;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

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 Create method creates a new process.
//The method returns an integer value
//that can be interpretted as follows:
//0 - Successful completion.
//2 - The user
//does not have access to the requested information.
//3 - The user does not have
//sufficient privilge.
//8 - Unknown failure.
//9 - The path specified does not
//exist.
//21 - The specified parameter is invalid.
//Other - For integer values
//other than those listed above, refer to Win32 error code documentation.

procedure  Invoke_Win32_Process_Create;
var
  objWMIService   : OLEVariant;
  objInvoker      : OLEVariant;
  objInParams     : OLEVariant;
  objOutParams    : OLEVariant;
begin
  objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',['.','root\CIMV2']));
  objInvoker    := objWMIService.Get('Win32_Process');
  objInParams   := objInvoker.Methods_.Item('Create').InParameters.SpawnInstance_();
  objInParams.CommandLine:='notepad.exe';
  objOutParams  := objWMIService.ExecMethod('Win32_Process', 'Create', objInParams);
  Writeln('ProcessId           '+VarStrNull(objOutParams.ProcessId));
  Writeln('ReturnValue         '+VarStrNull(objOutParams.ReturnValue));
end;

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

Added support for WMI events

Check the generated code for the __InstanceCreationEvent Event using as traget instance the Win32_Process class, this sample code check when new process is launched in the whole system.

//------------------------------------------------------------------------------
//     This code was generated by the Wmi Delphi Code Creator https://theroadtodelphi.wordpress.com
//     Version: 1.0.0.11
//
//
//
//     LIABILITY DISCLAIMER
//     THIS GENERATED CODE IS DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED.
//     YOU USE IT AT YOUR OWN RISK. THE AUTHOR NOT WILL BE LIABLE FOR DATA LOSS,
//     DAMAGES AND LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS CODE.
//
//
//------------------------------------------------------------------------------
program GetWMI_Info;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

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;

function KeyPressed:boolean; //Detect if an key is pressed
var
NumEvents   : DWORD;
ir          : _INPUT_RECORD;
bufcount    : DWORD;
StdIn       : THandle;
begin
    Result:=false;
    StdIn := GetStdHandle(STD_INPUT_HANDLE);
    NumEvents:=0;
    GetNumberOfConsoleInputEvents(StdIn,NumEvents);
    if NumEvents<> 0 then
    begin
        PeekConsoleInput(StdIn,ir,1,bufcount);
        if bufcount <> 0 then
        begin
            if ir.EventType = KEY_EVENT then
            begin
              if ir.Event.KeyEvent.bKeyDown then
                result:=true
              else
                FlushConsoleInputBuffer(StdIn);
            end
            else
            FlushConsoleInputBuffer(StdIn);
        end;
    end;
end;

Procedure  Event___InstanceCreationEvent_Target_Win32_Process;
var
  objWMIService   : OLEVariant;
  objEvent        : OLEVariant;
  objResult       : OLEVariant;
begin
  objWMIService := GetWMIObject('winmgmts:\\.\root\CIMV2');
  objEvent      := objWMIService.ExecNotificationQuery('Select * from __InstanceCreationEvent Within 1 Where TargetInstance ISA ''Win32_Process''    ');
  while not KeyPressed do
  begin
    try
     objResult := objEvent.NextEvent(100);
    except
     on E:EOleException do
     if EOleException(E).ErrorCode=HRESULT($80043001) then //Check for the timeout error wbemErrTimedOut 0x80043001
       objResult:=Null
     else
     raise;
    end;

    if not VarIsNull(objResult) then
    begin
      Writeln('Caption                  '+VarStrNull(objResult.TargetInstance.Caption));
      Writeln('CommandLine              '+VarStrNull(objResult.TargetInstance.CommandLine));
      Writeln('CreationClassName        '+VarStrNull(objResult.TargetInstance.CreationClassName));
      Writeln('CreationDate             '+VarStrNull(objResult.TargetInstance.CreationDate));
      Writeln('CSCreationClassName      '+VarStrNull(objResult.TargetInstance.CSCreationClassName));
      Writeln('CSName                   '+VarStrNull(objResult.TargetInstance.CSName));
      Writeln('Description              '+VarStrNull(objResult.TargetInstance.Description));
      Writeln('ExecutablePath           '+VarStrNull(objResult.TargetInstance.ExecutablePath));
      Writeln('ExecutionState           '+VarStrNull(objResult.TargetInstance.ExecutionState));
      Writeln('Handle                   '+VarStrNull(objResult.TargetInstance.Handle));
      Writeln('HandleCount              '+VarStrNull(objResult.TargetInstance.HandleCount));
      Writeln('InstallDate              '+VarStrNull(objResult.TargetInstance.InstallDate));
      Writeln('KernelModeTime           '+VarStrNull(objResult.TargetInstance.KernelModeTime));
      Writeln('MaximumWorkingSetSize    '+VarStrNull(objResult.TargetInstance.MaximumWorkingSetSize));
      Writeln('MinimumWorkingSetSize    '+VarStrNull(objResult.TargetInstance.MinimumWorkingSetSize));
      Writeln('Name                     '+VarStrNull(objResult.TargetInstance.Name));
      Writeln('OSCreationClassName      '+VarStrNull(objResult.TargetInstance.OSCreationClassName));
      Writeln('OSName                   '+VarStrNull(objResult.TargetInstance.OSName));
      Writeln('OtherOperationCount      '+VarStrNull(objResult.TargetInstance.OtherOperationCount));
      Writeln('OtherTransferCount       '+VarStrNull(objResult.TargetInstance.OtherTransferCount));
      Writeln('PageFaults               '+VarStrNull(objResult.TargetInstance.PageFaults));
      Writeln('PageFileUsage            '+VarStrNull(objResult.TargetInstance.PageFileUsage));
      Writeln('ParentProcessId          '+VarStrNull(objResult.TargetInstance.ParentProcessId));
      Writeln('PeakPageFileUsage        '+VarStrNull(objResult.TargetInstance.PeakPageFileUsage));
      Writeln('PeakVirtualSize          '+VarStrNull(objResult.TargetInstance.PeakVirtualSize));
      Writeln('PeakWorkingSetSize       '+VarStrNull(objResult.TargetInstance.PeakWorkingSetSize));
      Writeln('Priority                 '+VarStrNull(objResult.TargetInstance.Priority));
      Writeln('PrivatePageCount         '+VarStrNull(objResult.TargetInstance.PrivatePageCount));
      Writeln('ProcessId                '+VarStrNull(objResult.TargetInstance.ProcessId));
      Writeln('QuotaNonPagedPoolUsage   '+VarStrNull(objResult.TargetInstance.QuotaNonPagedPoolUsage));
      Writeln('QuotaPagedPoolUsage      '+VarStrNull(objResult.TargetInstance.QuotaPagedPoolUsage));
      Writeln('QuotaPeakNonPagedPoolUsage'+VarStrNull(objResult.TargetInstance.QuotaPeakNonPagedPoolUsage));
      Writeln('QuotaPeakPagedPoolUsage  '+VarStrNull(objResult.TargetInstance.QuotaPeakPagedPoolUsage));
      Writeln('ReadOperationCount       '+VarStrNull(objResult.TargetInstance.ReadOperationCount));
      Writeln('ReadTransferCount        '+VarStrNull(objResult.TargetInstance.ReadTransferCount));
      Writeln('SessionId                '+VarStrNull(objResult.TargetInstance.SessionId));
      Writeln('Status                   '+VarStrNull(objResult.TargetInstance.Status));
      Writeln('TerminationDate          '+VarStrNull(objResult.TargetInstance.TerminationDate));
      Writeln('ThreadCount              '+VarStrNull(objResult.TargetInstance.ThreadCount));
      Writeln('UserModeTime             '+VarStrNull(objResult.TargetInstance.UserModeTime));
      Writeln('VirtualSize              '+VarStrNull(objResult.TargetInstance.VirtualSize));
      Writeln('WindowsVersion           '+VarStrNull(objResult.TargetInstance.WindowsVersion));
      Writeln('WorkingSetSize           '+VarStrNull(objResult.TargetInstance.WorkingSetSize));
      Writeln('WriteOperationCount      '+VarStrNull(objResult.TargetInstance.WriteOperationCount));
      Writeln('WriteTransferCount       '+VarStrNull(objResult.TargetInstance.WriteTransferCount));
      Writeln('');
    end;
  end;
end;

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

Improved WMi explorer window

this option now shows more info about the wmi classes, including the properties types, methods parameters (in, out)

also includes an option to view the values of the properties of the selected WMI class

and finally a new option called Search WMI Database, this option lets you search in all the wmi classes for a particular word.

see this sample image looking for the Motherboard word

Download The installer from here

And don’t forget , wich all your comments and suggestions are very welcome.


21 Comments

WMI Delphi Code Creator

UPDATE

The new page of this project is hosted in Github.

Introducing the WMI Delphi Code Creator © tool allows you to generate delphi code that uses WMI to complete a management task such as querying for wmi data.

This freeware tool is inspired by the  WMI Code Creator.

Features

  • Create full delphi console project
  • Create a 100% functional delphi procedure wich encapsulates the logic to retrieve WMI information
  • Full access to metadata of any WMI Class registered in the system
  • direct link to MSDN web page containig a description of the WMI Class

Todo

  • support fo call WMI methods.
  • support for WMI events
  • remote WMI support
  • Support more programming languages (delphi-prism, C++ builder)
  • Dynamic execution of generated code.
  • and more….

Used tools for write this application

Recommended Links about WMI

Screenshots

This slideshow requires JavaScript.

sample code generated by the application

//------------------------------------------------------------------------------
//     This code was generated by the Wmi Delphi Code Creator
//     Version: 1.0.0.1
//
//
//
//     LIABILITY DISCLAIMER
//     THIS GENERATED CODE IS DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED.
//     YOU USE IT AT YOUR OWN RISK. THE AUTHOR NOT WILL BE LIABLE FOR DATA LOSS,
//     DAMAGES AND LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS CODE.
//
//
//------------------------------------------------------------------------------
program GetWMI_Info;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

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:='Null';
  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;

//
procedure  GetWin32_ShareInfo;
var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_Share','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
    Writeln(Format('AccessMask                     %s',[VarStrNull(colItem.AccessMask)]));
    Writeln(Format('AllowMaximum                   %s',[VarStrNull(colItem.AllowMaximum)]));
    Writeln(Format('Caption                        %s',[VarStrNull(colItem.Caption)]));
    Writeln(Format('Description                    %s',[VarStrNull(colItem.Description)]));
    Writeln(Format('InstallDate                    %s',[VarStrNull(colItem.InstallDate)]));
    Writeln(Format('MaximumAllowed                 %s',[VarStrNull(colItem.MaximumAllowed)]));
    Writeln(Format('Name                           %s',[VarStrNull(colItem.Name)]));
    Writeln(Format('Path                           %s',[VarStrNull(colItem.Path)]));
    Writeln(Format('Status                         %s',[VarStrNull(colItem.Status)]));
    Writeln(Format('Type                           %s',[VarStrNull(colItem.Type)]));
    Writeln('');
  end;
end;

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

Download from here.

Notice for Windows Vista, Windows 7 and Windows 2008  users, this application requires  run as administrator.

All your comments, suggestions and criticisms are very welcome.


4 Comments

Enumerating the restore points using WMI and Delphi

To Enumerate the restore points you can use the SystemRestore WMI Class

This class exposes five properties

  • Description : The description to be displayed so the user can easily identify a restore point
  • RestorePointType : The type of restore point.
  • EventType : The type of event.
  • SequenceNumber : The sequence number of the restore point.
  • CreationTime : The time at which the state change occurred.

Check this sample application

//Author Rodrigo Ruz 14/04/2010.
{$APPTYPE CONSOLE}

uses
  SysUtils
  ,ActiveX
  ,ComObj
  ,Variants;

function RestorePointTypeToStr(RestorePointType:Integer):string;
begin
     case  RestorePointType of
      0  : Result:='APPLICATION_INSTALL';
      1  : Result:='APPLICATION_UNINSTALL';
      13 : Result:='CANCELLED_OPERATION';
      10 : Result:='DEVICE_DRIVER_INSTALL';
      12 : Result:='MODIFY_SETTINGS'
      else
      Result:='Unknow';
     end;
end;

function EventTypeToStr(EventType:integer) : string;
begin
     case  EventType of
      102  : Result:='BEGIN_NESTED_SYSTEM_CHANGE';
      100  : Result:='BEGIN_SYSTEM_CHANGE';
      103  : Result:='END_NESTED_SYSTEM_CHANGE';
      101  : Result:='END_SYSTEM_CHANGE'
      else
      Result:='Unknow';
     end;
end;

function WMITimeToStr(WMITime:string) : string; //convert to dd/mm/yyyy hh:mm:ss
begin
    //20020710113047.000000420-000 example    source http://technet.microsoft.com/en-us/library/ee156576.aspx
    result:=Format('%s/%s/%s %s:%s:%s',[copy(WMITime,7,2),copy(WMITime,5,2),copy(WMITime,1,4),copy(WMITime,9,2),copy(WMITime,11,2),copy(WMITime,13,2)]);
end;

procedure GetRestorePoints;
var
  oSWbemLocator : OLEVariant;
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin
  oSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService := oSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  colItems      := objWMIService.ExecQuery('SELECT * FROM SystemRestore','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
      WriteLn(Format('%s %-15s',['Description',colItem.Description]));
      WriteLn(Format('%s %-15s',['RestorePointType',RestorePointTypeToStr(colItem.RestorePointType)]));
      WriteLn(Format('%s %-15s',['EventType',EventTypeToStr(colItem.EventType)]));
      WriteLn(Format('%s %-15s',['SequenceNumber',colItem.SequenceNumber]));
      WriteLn(Format('%s %-15s',['CreationTime',WMITimeToStr(colItem.CreationTime)]));
      Writeln;
      colItem:=Unassigned;
  end;
end;

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


4 Comments

Detect if an antivirus software is installed in Windows using C#

The next code shows how detect if an antivirus software is installed in Windows using C# and the WMI.

using System;
using System.Text;
using System.Management;

namespace ConsoleApplication1
{
  class Program
  {
    public static bool AntivirusInstalled()
    {

      string wmipathstr = @"\\" + Environment.MachineName + @"\root\SecurityCenter";
      try
      {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmipathstr, "SELECT * FROM AntivirusProduct");
        ManagementObjectCollection instances = searcher.Get();
        return instances.Count > 0;
      }

      catch (Exception e)
      {
        Console.WriteLine(e.Message);
      }

      return false;
    } 

    public static void Main(string[] args)
    {
      bool returnCode = AntivirusInstalled();
      Console.WriteLine("Antivirus Installed " + returnCode.ToString());
      Console.WriteLine();
      Console.Read();
    }


  }
}