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)