Introduction to WMI

Windows Management Instrumentation (WMI) is perhaps one of the best kept secrets in the IT world. It is a very powerful set of tools that you can use to gather information, configure settings, and manage PCs either locally or across the network. It truly is "management" and "instrumentation" of Windows.

WMI is the Microsoft implementation of an industry-based Web-Based Enterprise Management (WBEM) initiative. The Distributed Management Task Force (DMTF) now sets the standards for WMI.

There are WMI "namespaces" for performing operations on the registry, operations on the file system, discovering and configuring hardware, and manipulating settings of Windows itself. The system is extensible, so a new WMI "provider" can be added during the install of an application (like SQL Server or Internet Information Service). The default namespace (called "root\cimv2") contains over 500 classes in the following categories

The WMI data is accessed via a relational database. Like any database, there are schemas, data types, primary keys, table relationships, etc.

Documentation Links

Introduction

By way of introduction, let's just jump in with a simple example. The example below will return the amount of free disk space on the C: Drive of your PC. This example will use the VB.Net "ManagementClass" class to return information from the WMI "Win32_LogicalDisk" class. The GetInstances() method returns a collection of Win32_LogicalDisk instances, so we just loop through each one to find the one we want (in this example "C:").

The instance of the Win32_LogicalDisk class is like a database record, so we get the information contained in each field the same way we'd get information from a DataRow object (namely, using the object("FieldName") notation). And just like a DataRow, the field returns an "object" which we have to cast back to its native type. In this example, we convert the object returned from obj("FreeSpace") into a double. You'll need to consult the documentation for the Win32_LogicalDisk class to know the names of the fields and the data types in the "database".

Dim wmi As System.Management.ManagementClass
Dim obj As System.Management.ManagementObject
Dim ans As Double

wmi = New System.Management.ManagementClass("Win32_LogicalDisk")

For Each obj In wmi.GetInstances()
    If obj("DeviceID").ToString = "C:" Then
        ans = Convert.ToDouble(obj("FreeSpace")) / (1024.0# * 1024.0#)
        Exit For
    End If
Next

Return ans

To make this example work on a remote PC, you only have to change one line of code.

Dim wmi As System.Management.ManagementClass
Dim obj As System.Management.ManagementObject
Dim ans As Double

' The argument to ManagementClass includes 3 things:
' 1) the name of remote PC
' 2) the WMI "Namespace" (\root\cimv2:)
' 3) the WMI Class (Win32_LogicalDisk)
wmi = New System.Management.ManagementClass("\\" & RemotePC & "\root\cimv2:" & _
 "Win32_LogicalDisk")

For Each obj In wmi.GetInstances()
    If obj("DeviceID").ToString = "C:" Then
        ans = Convert.ToDouble(obj("FreeSpace")) / (1024.0# * 1024.0#)
        Exit For
    End If
Next

Return ans

Note: There are some obvious requirements for security, permissions, and firewall settings that must be met before performing any WMI operations on a remote PC.

So far ,the example is just looping through all of the instances of Win32_LogicalDisk to find the correct one (to find the "C:" drive letter). WMI also supports its own query language, called WMI Query Language (WQL), that allows you to ask for a specific instance rather searching through the collection to find a match. Take a look at the modified version of the example:

Dim ms As System.Management.ManagementScope
Dim oq As System.Management.ObjectQuery
Dim mos As System.Management.ManagementObjectSearcher
Dim obj As System.Management.ManagementObject
Dim ans As Double

' The "scope" includes the name of the PC and the WMI namespace
ms = New System.Management.ManagementScope("\\" & RemotePC & _
 "\root\cimv2")

' use WQL to get just the one instance we want.  This should look familiar
' to those who are used to SQL
oq = New System.Management.ObjectQuery("SELECT * FROM Win32_LogicalDisk" & _
 WHERE DeviceID='C:'")

' execute the query
mos = New System.Management.ManagementObjectSearcher(ms, oq)
For Each obj In mos.Get
    ans = Convert.ToDouble(obj("FreeSpace")) / (1024.0# * 1024.0#)
    Exit For
Next

Return ans

This example is much more efficient. If you don't believe me, put a floppy in drive A: and then execute the original example... it will take several seconds to scan the floppy to determine its free space before WMI gets around to looking at drive B: and then C:. This shortcuts the system to return just the instances that meet the criteria in the WQL query.

But wait, there's more! (said in a infomercial tone of voice). The records in WMI are truly a database and therefore have primary keys. So, when we consult the documentation, we see that the DeviceID field is a primary key. Now, we can short cut the system even more... since primary keys are guaranteed to be unique, we can ask for that specific instance "by name" rather than looping. Take a look at this shortened version:

Dim obj As System.Management.ManagementObject
Dim ans As Double

obj = New System.Management.ManagementObject("\\" & RemotePC & _
 "\root\cimv2:Win32_LogicalDisk.DeviceID='C:'")
ans = Convert.ToDouble(obj("FreeSpace")) / (1024.0# * 1024.0#)

Return ans

Note: If you know the primary key and only want that one instance, you can ask for a specific instance of the management object.

Let's look at different example to demonstrate the concept of "WMI Associations". This example uses the Win32_UserAccount class to create a list of local accounts with a few of their attributes, then we get the related "rows" of information from the Win32_Group class. This allows us to get the group membership for each account. Again, you have to consult the documentation to determine if a relationship exists between the Win32_UserAccount and Win32_Group classes.

Dim wmi As System.Management.ManagementClass
Dim obj As System.Management.ManagementObject
Dim sb As New System.Text.StringBuilder

' create a WMI object based upon a simple "path"
wmi = New System.Management.ManagementClass("\\" & RemotePC & _
 "\root\cimv2:Win32_UserAccount")

' iterate through the instances
For Each obj In wmi.GetInstances()
    sb.Append("Name: " & obj("Name").ToString & vbCr)
    sb.Append("Caption: " & obj("Caption").ToString & vbCr)
    sb.Append("Description: " & obj("Description").ToString & vbCr)
    sb.Append("SID: " & obj("SID").ToString & vbCr)

    ' Now let's use a "data relationship" to get associated data from
    ' another class
    sb.Append("Groups: ")
    For Each related_obj In obj.GetRelated("Win32_Group")
        sb.Append(related_obj("Name").ToString & ", ")
    Next
    sb.Append(vbCr)
Next

Return sb.ToString

WMI is not just about obtaining read-only data from a system, it can also be used to interact and control a system. This next example uses the InvokeMethod() method of the Win32_OperatingSystem class to shutdown a remote PC. Notice the use of a new class called ManagementBaseObject that is used to pass parameters and return codes to and from the WMI class. There is some initial "prep work" using the GetMethodParameters() method to set the number and types of parameters.

Dim wmi As ManagementClass
Dim inParams, outParams As ManagementBaseObject
Dim obj As ManagementObject

Const LOGOFF As Integer = 0
Const REBOOT As Integer = 2
Const SHUTDOWN As Integer = 8
Const FORCEACTION As Integer = 4

wmi = New ManagementClass("\\" & RemotePC & "\root\cimv2:Win32_OperatingSystem")
For Each obj In wmi.GetInstances()
    ' Get an input parameters object for this method
    inParams = obj.GetMethodParameters("Win32Shutdown")

    ' fill 'em in
    inParams("Flags") = SHUTDOWN + FORCEACTION
    inParams("Reserved") = 0

    ' do it!
    outParams = obj.InvokeMethod("Win32Shutdown", inParams, Nothing)
    Result = Convert.ToInt32(outParams("returnValue"))
Next

The meaning of the returns code from the Win32Shutdown method are found in the documentation.

Additional Tools

The following tools from Microsoft are very useful in exploring the WMI universe. Of particular note is the "WMI Object Browser" from the WMI Administrative Tools download.

API Documentation Links

Downloads/Links

Download the VB.Net Source code examples used in this article: WMI_Examples.zip and Logoff.zip
Read the related article on Son of Snoop on Steroids