Build a WMI Relationship Browser
|Figure 6. The WMI Relationship Browser: The figure shows a dropdown list of all running instances of the class entered into the Class field, as well as a detailed listing of all objects related to a selected instance.|
The second sample application is a tiny WMI Browser. Users can enter the name of a WMI class to get all running instances of this class, and then select an instance to show items related to that instance. Note that the browser uses the instances to show related objects rather than related classes. All instances of a class have the same related classes, but separate instances of the same class often have different related objects, because they're related to different components of a system. For example, the CPU and the graphics adapter are both instances of a Win32_PnPEntity, but the graphic adapter is related to a monitor, while the CPU isn't. Figure 6
shows the mini-browser in action.
When you type in a class name and click the Get Instances button, the browser fills the combo box in the Instances section of the form with a list of instance paths. The code generates each instance path by concatenating the class name, a dot and a comma-separated list of key-value pairs with the key values of our instances. The key property will be the Name
property on most objects, although there are several objects with different keys. For example, in Figure 6
, the key property is the DeviceID
. In contrast, a Win32_UserAccount (not shown) has two keys: Name
. When you select an instance, the browser fills the Related Objects section of the form with information that's retrieved using the GetRelated()
Here's the code that fills the Instances combo box. You'll find some new WMI elements that you haven't seen before in this code:
ManagementClass mc = new ManagementClass(txtClassName.Text);
foreach (ManagementObject mo in mc.GetInstances())
string s = mo.ClassPath.ClassName;
foreach(PropertyData pd in mo.Properties)
s += ".";
s += ",";
s+= pd.Name + "=";
s += "'" + pd.Value + "'";
s += pd.Value;
|Author's Note: The error handling code is omitted in the code fragment above, but you can find it in the accompanying sample source.
The preceding code first creates a ManagementClass instance (mc
), passing the class name the user entered into the Class TextBox to the constructor. Next it clears the Instances ComboBox and iterates over all available instances of the mc
ManagementClass, filling the ComboBox with the unique instance identification strings.
To find the key properties for each instance you iterate through all its properties, checking whether the property qualifier "key" is set to true
. Non-key properties don't have this qualifier, so the code throws an exception when the qualifier doesn't exist. As you're not interested in non-key properties, you can simply ignore the exception in the catch
part of the try/catch
The code outputs a dot (.
) to separate the instance name from the first key/value property pair, but uses a comma for all subsequent properties. It also adds surrounding single-quotes to the value when the key type is string.
Clicking the "Show Related" button causes the application to display detailed information about the selected item in the multi-line TextBox in the Related Objects section of the form. Listing 1
shows the code in the OnClick event handler of the button.
The code clears the txtRelated
TextBox, displays an hourglass cursor, and then creates a new ManagementObject instance using the unique instance string from the item selected in the cbInstances
ComboBox. Then it calls the Get()
method to acquire the instance from the WMI repository. It then calls the GetRelated()
Method and iterates over all the related objects found in the repository. The code doesn't test in advance for any particular type of objectyou won't necessarily know what kind of objects the GetRelated()
method will retrieve. That means it must process the retrieved objects generically.
Consider how to display the name of a retrieved object: it's simple, just add the value of the ClassPath
property to the output. But we don't want the full ClassPath
value, just the last part of itthe class name. You could split the string manually, but that's too much trouble, even using the convenient System.String functions. If you look carefully you'll see that ClassPath is of type ManagementPath, a neat helper class that stores each part (server, namespace, class name, etc.) of the class path as individual members.
Now that you can get the object's name using ClassPath.ClassName
, the next step is to loop through all the system properties and add their names, values and types to the output. That's straightforward, and you can study Listing 1
to see how it's done. Retrieving the non-system properties is more interesting, because although the basic operation is the same, you need to check a few extra members.
For example, if the property is an array than the IsArray
qualifier will return true
, and you can iterate through the array members. Although this is a qualifier, unlike other qualifiers such as the "key" qualifier shown earlier, the IsArray
qualifier is mapped directly to Properties in the .NET classes and therefore you can access it directly via a member of the PropertyData object. The PropertyData object represents a collection of properties of management classes. Be careful when iterating over properties with multiple values! If the IsLocal
qualifier returns true
then the property has been defined in this class instance; otherwise the property is inherited from some base class.
Displaying methods is a little harder. Because methods are not specific to a certain instance but are common to all instances of the same class, you can't find the method collection in the ManagementObject class. Instead, you must use the ManagementClass, which has a collection of MethodData objects called (appropriately enough) methods
that contain all the necessary information about method names, parameters and calling conventions. The CIM defines the input and output parameters for a method as a ManagementObject though. You can find the parameter name and type in the properties collection of the InParameters
(or respectively OutParameters
) object. Don't forget that methods exist with neither input
nor output parameters. The ManagementObject representing these missing parameters is null
Bear in mind that acquiring such detailed information from the CIM requires heavy interaction with the running operating system and can drastically affect the performance of your system. Don't ever try to run this in an inner loop of an application; you'll wait for ages for it to finish such WMI tasks. Always remember that WMI is an implementation for Web Based Enterprise Management which presupposes that you will use it only for management purposes and primarily on distributed managed systems.
If you find yourself tempted to use WMI functionality on a local system you'll generally find methods in the Platform SDK or somewhere else in the Win32 environment. that will better fit your needs. Although WMI may seem to be the simplest way to find information about your system, in many cases it's slower than using equivalent Win32 API calls. For example, it's much faster to list users and their associated groups using the ADSI SDK than using the WMI. While WMI is the most universally applicable solution to almost any given management problem, it isn't always the fastest solution.