WBEM What?

Dr. Scripto

Hi Everyone. Welcome to the third edition of Scripting Eye for the GUI Guy. (Check out the Scripting Eye for the GUI Guy Archives to see previous editions.) In this issue we’re going to start examining a Windows GUI tool that, we suspect, some of you may not have encountered before. It’s a GUI tool named wbemtest that is buried deep in the bowels of Windows. The tool itself is a small treasure - the scripting possibilities it leads to are pure administrative gold. We think you’ll have fun with this one. In any case, email us at scripter@microsoft.com (in English, if possible) and let us know. We can’t promise to respond to all of the requests we receive, but we’ll do our best.

On This Page

WBEM What?
Making the Connection
Getting Class Names
Getting Properties and Methods
Getting Property Data Types
A New Challenge

WBEM What?

There’s no time for preamble in this issue people. Click Start->Run. In the resultant text box labeled Open, type wbemtest.exe. Even you GUI folks might not recognize the beast that your incantations have summoned forth:

wbemtest screenshot

This abomination before your eyes is the Windows Management Instrumentation Tester. Those in the know refer to it by the name of its executable, wbemtest (web-em-test). It isn’t pretty, but it sure is useful. In fact, wbemtest, or something like it, is an essential tool for system admin scripters. It allows you to peer into the depths of the WMI repository to divine what powers you might be able to harness in your scripts.

The word “Tester” in the title of this opening dialog should serve as a warning - even to GUI gurus. Wbemtest was not designed for mere mortals - or even savvy system admin scripters. It was designed as a tool for testing the functionality of the WMI COM API. The result is that: 1) wbemtest enables you to do almost anything with the WMI COM API and 2) the interface is difficult to use - even for experts.

The Help button does work and the help provided does a good job of explaining the functionality exposed by each of the buttons. That said, it’s difficult to learn the ins-and-outs of wbemtest by reading reference information, you really need a walkthrough of some typical usage scenarios. That’s exactly what we’re going to do in this issue of GUI Guy. Of course, as we walk through the GUI, we’ll be showing you how to do the same things within a script. By the end of the article, you’ll know enough about the GUI tool to make some use of it and you’ll have learned enough about the corresponding scripting story that, we hope, you’ll go off and hand-craft your very own customized version of the tool.

Making the Connection

You may have noticed that there are quite a number of buttons on the opening dialog of wbemtest, but few of them are active. That’s wbemtest’s way of indicating that you haven’t yet attached to a WMI repository. Yes, it would be better if wbemtest prompted you for the WMI repository you would like to connect to - but it doesn’t. So, to get started, click on the Connect button. You should see the following dialog:

wbemtest screenshot

This dialog is also pretty intimidating. Luckily, making a connection is as easy as pressing the Connect button. But let’s not be too hasty. As you may know, when you “connect to WMI,” you also specify the particular namespace or section of the WMI repository to connect to. Were you to press the Connect button as things stand, you would be connected to the root\default namespace. Although the default namespace houses some useful stuff, we are more often than not interested in connecting to the root\CIMV2 namespace. Go ahead and change root\default to root\CIMV2.

Note

I bet you’re curious about what CIMV2 means, right? Well for 12 easy monthly payments of just…Oh, okay. The class model in the WMI repository (and the whole premise behind WMI) is based upon work done by an organization called the Distributed Management Task Force, or DMTF. They call the class model the Common Information Model, or CIM, and WMI is based upon version 2 (V2) of the model. You can learn more at the DMTF website.

Hold off just a little longer on pressing the Connect button. If you were to press it now, you would be achieving, to a rough approximation, the same thing as running the following one-line script:

Set objWMIService = GetObject("winmgmts:")

You’re probably wondering where in that one line we indicated that the script should connect to the root\CIMV2 namespace. When you script, some of the default behaviors are different than the defaults used by wbemtest. As you saw when we opened the Connect dialog, root\default came up as the default namespace. Scripts, on the other hand, use root\CIMV2 as the default. The default namespace used by scripts is configurable and is stored in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM\Scripting in the Default Namespace key. Note that, as you can see in the following screen shot, something called the Default Impersonation Level is also stored here. The magical number 3 stands for “Impersonate” - the same default used by wbemtest (see the bottom left corner of the screenshot above).

registry editor screenshot

WMI scripts use the Distributed COM (DCOM) protocol when accessing remote machines. DCOM includes a number of configuration options and one of these, Impersonation Level, determines how DCOM can use the security credentials under which you are running your WMI script. An impersonation level of Impersonate is almost always the appropriate choice for your WMI scripts. The lower setting, Identify, won’t allow the script to do much useful work and the higher setting, Delegate, requires you to make configuration changes to Active Directory for each user and computer that will play a part in running the script.

Note

In versions of WMI available for Windows NT 4, the default impersonation level was Identify rather than Impersonate. You can, however, override the default by explicitly setting the impersonation level within a script to Impersonate. These earlier versions of WMI are one reason we explicitly set the impersonation level on scripts in the Script Center.

The following script snippet specifies both the namespace to connect to, root\cimv2, and the impersonation level that DCOM should use:

Set objWMIService =  GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

There are quite a few other connection options configurable from the Connect dialog, but we’re going to be content, for now, with our basic understanding of namespaces and impersonation levels. Go ahead and press the Connect button. You should be rewarded with the following dialog.

screenshot of wbemtest

Note the indication, under the Namespace label, that we are now connected to the root\cimv2 namespace. The buttons grouped together under the mysterious “IWbemServices” label are now ready to be used - if only we can figure out what exactly they do. The names of the buttons in the first two columns give us a hint. All the button names in the first column include the word Class or Classes and all the buttons in the second column include Instance or Instances. In this article we’re going to take a look at classes.

Getting Class Names

Scripters often need to see a list of the WMI classes available in a namespace. There are various ways to do this. Our favorite is the Scriptomatic tool. However, wbemtest can provide you with the same information and wbemtest has an advantage over the Scriptomatic; wbemtest is like Notepad - it’s installed by default on pretty much the entire Windows family. So, you can count on it being available when you need it. To use wbemtest to get a list of WMI classes, start by clicking on the Enum Classes button, conveniently located in the upper left corner of the dialog box. You’ll be greeted by a Superclass Info dialog box. Don’t enter a superclass. By leaving this field blank that effectively says you want to start at the root of the class hierarchy. Since you want to retrieve all of the available classes, select the radio button labeled Recursive.

screenshot of wbemtest

Now for the exciting conclusion: press OK. You should see a Query Result dialog that looks something like this one:

screenshot of wbemtest

The class names you see listed all begin with a double underscore. Although there are occasions when scripters are interested in these system classes, we are usually more interested in ones with the Win32_ prefix. So, either click in the list box and type “win32_” or scroll down until you see the Win32_ classes.

wbemtest screenshot

Scrolling through this list and looking at the names of the classes is a good way to begin familiarizing yourself with what’s possible with WMI scripting. Although the fact that there is a Win32_Account class doesn’t guarantee that you can do what you need to with an account, it does give you some hope and a place to start.

Now let’s build upon our connection script and get it to perform this same query to return these class names.

Set objWMIService = _ 
   GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

Set colClasses = objWMIService.SubClassesOf

For Each objClass In colClasses
   WScript.Echo objClass.Path_.Class
Next

Here’s a screenshot of a section of the output from the above script:

screenshot of cmdline output

You’ll notice that the SubClassesOf method doesn’t return the class names in alphabetical order like wbemtest does, but we can modify our script to do this. One way is to use a disconnected ADO recordset, as in the following script. To learn more about using disconnected recordsets for sorting, check out the Sorting Data by Using a Disconnected Recordset section of the Windows 2000 Scripting Guide.

Const adVarChar = 200
Const MaxCharacters = 255
Const adUseClient = 3
Const adOpenStatic = 3
Const adLockBatchOptimistic = 4

Set objWMIService = _ 
   GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

Set colClasses = objWMIService.SubClassesOf

Set rsClasses = CreateObject("ADODB.Recordset")
rsClasses.Fields.Append "ClassName", adVarChar, MaxCharacters

With rsClasses
        .ActiveConnection = Nothing
        .CursorLocation =   adUseClient
        .CursorType =       adOpenStatic
        .LockType =         adLockBatchOptimistic
        .Open
End With

For Each objClass In colClasses
   rsClasses.AddNew
   rsClasses("ClassName") = objClass.Path_.Class
   rsClasses.Update
Next

rsClasses.Sort = "ClassName"

rsClasses.MoveFirst
Do Until rsClasses.EOF
 Wscript.Echo rsClasses.Fields.Item("ClassName")
rsClasses.MoveNext
Loop

One advantage of using a script instead of wbemtest is that we can customize the output any way we wish. Suppose we want to display only classes that begin with the Win32_ prefix; that’s fairly easy to achieve. A very straightforward solution is to retrieve all of the classes and just filter the output, displaying only those class names that begin with the six characters Win32_. That’s what the following script does.

Set objWMIService = _ 
   GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

Set colClasses = objWMIService.SubClassesOf

For Each objClass In colClasses
If Left(objClass.Path_.Class,6) = "Win32_" Then
   WScript.Echo objClass.Path_.Class
End If
Next

You can do anything you like with this information - sort it in whatever order you prefer, filter it as you wish and save it or display it any way you want. That’s the flexibility you gain by scripting tasks that are already covered by GUI tools.

Getting Properties and Methods

After finding a WMI class that looks promising, you’ll likely want to drill down a little further and find out if the class has properties or methods that appear to expose the functionality you require. In wbemtest, you display a list of properties and methods by double-clicking on the class of interest in the Query Result dialog box. Here’s a screenshot showing the result of double-clicking on Win32_PageFile.

wbemtest screenshot

Note that you might see different properties than those in the screenshot. That’s because we’ve checked the Hide System Properties check box, which hides the double-underscore system properties. Many people are thrown off by the <null>’s in the property list. These nulls are just placeholders, indicating that you’re looking at the properties of the class, not values of the properties corresponding to particular instances of the class. If you’re not familiar with the concepts of classes and instances, just think of it like this: the above screenshot is displaying what values it is possible to return, not any of the actual values. It’s a way of letting you know what’s possible, not what is.

Here’s the corresponding script. We’ve hardcoded the Win32_PageFile class into the script, but if you’re interested in a different class, simply change the value assigned to the strClass variable to the name of the class you’re interested in. So, for example, if you want to switch to the Win32_BIOS class, you would modify the first line of the script to read strClass = “Win32_BIOS”. That’s the only change you would need to make.

strClass = "Win32_PageFile"

Set objWMIService = _ 
   GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

Set colClasses = objWMIService.ExecQuery("SELECT * FROM " & strClass)

For Each objClass In colClasses
   WScript.Echo "* Properties *"
   For Each objProperty In objClass.Properties_
      WScript.Echo objProperty.Name
   Next

   WScript.Echo vbNewLine & "* Methods *"
   For Each objMethod In objClass.Methods_
      WScript.Echo objMethod.Name
   Next
Next

Getting Property Data Types

In the wbemtest display of properties, the type of each property is listed next to the property name. The odd looking CIM_UINT32, for instance, indicates that the corresponding property is a number - a 32-bit integer. We may need that information at some point, so let’s have our script display it. This sort of auxiliary information about a property is stored in the Qualifiers collection associated with each property. The qualifier that contains the data type is named “CIMTYPE”. So, to retrieve the data type of each property, we use the Qualifiers_ property to retrieve the corresponding Qualifiers collection, and then use a For Each loop to iterate through those qualifiers. We use an If statement to filter for the CIMTYPE qualifier. The value of that qualifier is the type of the property.

strClass = "Win32_PageFile"

Set objWMIService = _
   GetObject("winmgmts:{impersonationLevel=impersonate}root\cimv2")

Set colClasses = objWMIService.ExecQuery("SELECT * FROM " & strClass)

For Each objClass In colClasses
   WScript.Echo "* Properties *"
   
   For Each objProperty In objClass.Properties_
      strPropertyOutput = objProperty.Name 
      For Each objQualifier In objProperty.Qualifiers_
         If objQualifier.Name = "CIMTYPE" Then
            strPropertyOutput =  strPropertyOutput & _
            ":" & objQualifier.Value
         End If
      Next
      WScript.Echo strPropertyOutput
   Next
 
   WScript.Echo vbNewLine & "* Methods *"
   For Each objMethod In objClass.Methods_
      WScript.Echo objMethod.Name
   Next
Next

Here’s a screenshot of the output of the script.

screenshot of command line output

Note that we used a variable, strPropertyOutput, to store the information about each property before displaying it using WScript.Echo. We did that for formatting reasons; WScript.Echo always appends a carriage return-linefeed to its output and we didn’t want to skip a line between the property’s name and its data type. We could have used WScript.StdOut.Write instead - but then our scripts would work only when run under the cscript.exe host, and we felt introducing the variable was a better choice.

A New Challenge

We’re going to wrap up this issue of GUI Guy with a challenge for you and some advertising for the next issue. You now have a snippet of code that returns WMI class names and another snippet that returns the properties and methods of a class. The challenge is to bring these two scripts together. Ironically, the best way to do this might be a GUI. We suggest you check out the HTA Developers Center section of the Script Center.

Hint: Maybe an HTA with a dropdown list and a display area would work. You could populate the dropdown list with class names and the display area with the corresponding properties and methods.

If you made it to the end of this issue, you really should read the next one. Not only will we explore a solution to our challenge, but we’ll start looking at instances of WMI classes. This is where the real utility of WMI begins to emerge. Those of you who are fans of the Scriptomatic utility should definitely join us - we’re going to learn enough to build a scaled-down Scriptomatic! How’s that for a sales pitch? We hope you enjoyed this issue and look forward to your feedback and suggestions.