WMI in Delphi and VBScript

Purpose

The purpose of this article is to provide a very basic introduction to WMI for Delphi developers who are unfamiliar with it. It contains example code to query WMI using Delphi.

What is WMI?

WMI (Windows Management Instrumentation) is a technological invention of Microsoft, whose purpose is to take care of the different operational environments of Windows. Or more precisely, as per Wikipedia:

Windows Management Instrumentation (WMI) consists of a set of extensions to the Windows Driver Model that provides an operating system interface through which instrumented components provide information and notification. WMI is Microsoft‘s implementation of the Web-Based Enterprise Management (WBEM) and Common Information Model (CIM) standards from the Distributed Management Task Force (DMTF).

It comes preinstalled since Windows 2000, and there are downloads available for Windows NT, Windows 95, and Windows 98. It has been around for a long time. You can learn all about it from the official Microsoft site, see here. Pluralsight also has a course available here. I’m sure there are lots of videos on YouTube as well.

What is it used for?

It is essentially used to help manage a Windows PC/network environment. You can use it to query information about PCs on the network, switch off monitors, listen for events such as a network controller going offline etc. It is extremely useful.

It’s been about 15 years since I last used it but recently someone piqued my interested again, so I took that as an excuse to revisit it. I absolutely love using technology like WMI.

Powershell

The best way to dive into WMI is to use Powershell. Here, I’m displaying my connected monitors:

ps1

And here, I’m enumerating my video controllers:

ps2

There’s so much information and it is extremely accessible. To get started, there are many tutorials online and plenty of books like PowerShell and WMI to take you deeper.

Enter VBScript

Back in the old days, before the likes of Powershell, VBScript was the simplest way to manipulate COM objects, whether on the command line or via ASP (Active Server Pages). Having played a little with PowerShell, my nostalgia led me back to VBScript. Here is a sample script I used to query some monitor related information about my PC:

On Error Resume Next

strComputer = "."
Set objCIMService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\wmi")

Wscript.Echo "connections:"

Set connections = objWMIService.ExecQuery("Select * from WmiMonitorConnectionParams")
for each connection in connections
  Wscript.Echo "Name: " & connection.InstanceName & " Active: " & connection.Active
Next

Wscript.Echo ""
Wscript.Echo "monitors:"

Set desktopMonitors = objCIMService.ExecQuery("Select * from CIM_DesktopMonitor")
for each monitor in desktopMonitors
  Wscript.Echo "Name: " & monitor.PNPDeviceID & " Device Id: " & monitor.DeviceID
Next

Wscript.Echo ""
Wscript.Echo "controllers:"

Set controllers = objCIMService.ExecQuery("Select * from CIM_VideoController")
For Each controller in controllers
  Wscript.Echo "DeviceID: " & controller.Caption    
Next

Wscript.Echo ""
Wscript.Echo "display params:"

Set displays = objWMIService.ExecQuery("Select * from WmiMonitorBasicDisplayParams")
for each display in displays
  Wscript.Echo "Name: " & display.InstanceName & " Active: " & display.Active
Next

Wscript.Echo ""
Wscript.Echo "ids:"

Set ids = objWMIService.ExecQuery("Select * from WMIMonitorID")
for each id in ids
  Wscript.Echo "Name: " & id.InstanceName
Next

CScript

Essentially I’m just selecting information I am interested in, iterating over each result returned, and displaying specific properties. The easiest way to execute the script is via the Console using CScript.exe which writes to the console rather than displaying a popup dialog for each message:

c:\Source>cscript wmi.vbs

And here are the results of executing this simple script:

wmiexample2

Delphi’s Turn

Delphi has always had great support for Microsoft’s COM technologies, so it was no surprise it was extremely simple to replicate the VBScript in Delphi. For anyone interested, the code is available here on Github. It’s a very simple console application.

The class TWmi, located in the uWmi.pas file, is responsible for connecting to WMI and executing a query. It uses a visitor-like pattern to call back a procedure on each item.

The constructor configures the WMI providers:

constructor TWmi.Create;
begin
  FLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWmiService := FLocator.ConnectServer('localhost','root\wmi');
  FCmiService := FLocator.ConnectServer('localhost', 'root\cimv2');
  FDefaultService := FLocator.ConnectServer('localhost', 'root\default');
end;

And the Run function does the rest of the work:

procedure TWmi.Run(AVisitor: TVisitor; ANamespace: TWmiNamespace; AQuery:string);
var
  Items: OLEVariant;
  Item: OLEVariant;
  Enum: IEnumVariant;
  Value: LongWord;
begin
  case ANamespace of
    nsWmi:      Items := FWmiService.ExecQuery(AQuery);
    nsCmi:      Items := FCmiService.ExecQuery(AQuery);
    nsDefault:  Items := FDefaultService.ExecQuery(AQuery);
  end;

  Enum := IUnknown(Items._NewEnum) as IEnumVariant;

  while Enum.Next(1, Item, Value) = 0 do begin
    AVisitor(Item);
    Item := Unassigned;
  end;
end;

From WinExample.dpr we simply use TWmi:

TWmi.Select(ShowMonitorId, nsWmi, 'Select * from WMIMonitorID');

And the specified callback function is invoked on each item:

procedure ShowMonitorId(AItem: OLEVariant);
begin
  WriteLn(#9 + Format('%s: %-15s', ['Name', AItem.InstanceName]));
end;

Conclusion

There is so much more you can do with WMI, such as listening for the WIFI controller to disconnect so you can respond appropriately in your application. This is just a very basic introduction, and a chance for me to reminisce and have some fun 🙂 I hope you may find this post useful.

Here is the output of the Delphi code, matching that of the VBScript.

wmiexample

Update

There is an open source project on Github here that allows you to generate Object Pascal, Oxygene, C++ and C# code to access the WMI classes, events and methods. It also includes a set of tools to explore and query the content of the WMI. Definitely worth checking out.

 

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s