The Road to Delphi

Delphi – Free Pascal – Oxygene

Be careful when you import the Microsoft WMIScripting Library

4 Comments

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.

Author: Rodrigo

Just another Delphi guy.

4 thoughts on “Be careful when you import the Microsoft WMIScripting Library

  1. Thanks a lot.

    Your article just saved my day. We are facing such a problem in one of our software compiled in Win7. Now I know how to solve it without pulling my hair out! ;)

    Hats off to you.

  2. Be it Microsoft, Adobe, etc: I have been bitten a few times as well :-)

    The lesson I learned in the past is that you should import any TLB on the ‘oldest’ system you want to support.

    Oh: and be prepared that some vendors don’t support older interfaces on newer products.
    Some times, late binding is the only solution to that.

    –jeroen

  3. Its sad that the words “immutable interface” doesn’t apply to certain vendors.

  4. Pingback: Accesing the WMI from Pascal Code (Delphi, Oxygene, FreePascal) « The Road to Delphi – a Blog About Delphi Programming (mostly)

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s