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.

November 29, 2010 at 3:01 am
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.
November 29, 2010 at 5:38 am
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
November 29, 2010 at 5:45 pm
Its sad that the words “immutable interface” doesn’t apply to certain vendors.
Pingback: Accesing the WMI from Pascal Code (Delphi, Oxygene, FreePascal) « The Road to Delphi – a Blog About Delphi Programming (mostly)