WMI Programming with Visual Basic.NET: Tips and Tricks
In my previous article, part four of this series, we looked into managing OS using WMI methods. In this article, we will look into some tips and tricks for using WMI.
You can download the zip of entire Visual Studio.NET solution (developed for this article) here.
The first part of my series gives you the details for creating a Visual Studio.NET solution for WMI from scratch. It will not be repeated anymore in any of the future articles in this series.
Can we start another application (process) through WMI?
In all of my examples in the previous part, we worked with windows services (or system services). Now I will start talking about processes. A process is something like a thread which starts execution in its only memory area (application domain). In general, every application starts in the form of a process. Almost all processes can be viewed using the task manager. Some processes may be system processes which run in the background (without having a user interface). Every process gets identified by a process id internally. We can get the full list of processes by using WMI class “Win32_process”. You can refer to part three to get an entire example of listing processes.
Now, the question is, can we start our own process (or even an application) using WMI? I hope anyone who works with .NET would definitely know how to do it using the native .NET framework. But now, I would like to do the same thing using WMI. The following program fragment demonstrates this:
' Get the object on which the method will be invoked
MessageBox.Show("Could not Start, Returned code:" & obj.ToString)
EndIf
A brief explanation of above program is included below.
We create a new process by using WMI class “Win32_Process”. There exists a method “create” within the class “Win32_Process”. That method creates a process at the OS through WMI and not our application. We provide parameters to that object using "inParams" (such as which application to execute as part of the process).
We execute the “create” method using the “InvokeMethod” of the “ManagementClass” object (currently the object points to a new process). The result of the “InvokeMethod” is returned into “outParams”. If the result is zero, it has successfully managed to invoke an external process. A non-zero result is a failure which would be shown through a message box. A detailed explanation of every return code is provided in the WMI SDK documentation.
You can understand the above program very easily, if you go through my previous article (part four). All information about calling a WMI method (including the “InvokeMethod” member) was discussed in detail in that article.
In all of my previous parts of this series, I specified the properties I wanted to display manually. In order to do that, we should check with the WMI SDK documentation to get the full list of properties (and of course descriptions) of a particular WMI class. Now, is there any way to display all the properties at one time?
The answer is yes. We do have a way to display all the properties (along with values) of almost any WMI class dynamically. Before we implement it practically, I need to introduce two of the new classes of the “System.Management” namespace:
PropertyDataCollection
PropertyData
Using the “Properties” property of the “ManagementObject” class, we transfer all properties (along with values) to an object reference of type “PropertyDataCollection”. We extract each and every property of information from “PropertyDataCollection” by using “PropertyData”. The following program illustrates the same.
The above program gets all of the properties along with their values into a data table, and finally gets them displayed through a data grid. The next section gives you a detailed explanation of the above program.
The above statement retrieves only a single instance of the “Win32_LogicalDisk” class based on the filter ‘deviceID=c:’. This excludes the use of unnecessary objects of type “ManagementObjectCollection”, as we are working with only a single instance.
The above statement retrieves all properties along with all values using “disk.Properties” and stores them in the “diskProperties” object.
DimdiskPropertyAsPropertyData
The above statement declares “diskProperty” to access each and every property from “diskProperties” collection.
ForEachdiskPropertyIndiskProperties
DimdrAsDataRow = dt.NewRow
dr("Name") = diskProperty.Name
dr("Value") = diskProperty.Value
dt.Rows.Add(dr)
NextdiskProperty
The above loop iterates through each and every property existing in the “diskProperties” collection using “diskProperty” and assigns property information to a new individual data row, which finally gets added to the data table.
dt.AcceptChanges()
The above statement ensures that the data table is filled with all of the contents from memory.
Me.DataGrid1.DataSource = dt
The above statement displays all of the information available in the data table through a data grid.
The above example demonstrates working with only one instance. How can I work with more than one instance simultaneously? In other words, I would like to have each of the device details presented as a single row of information (columns being all the properties and not the rows, as demonstrated previously).
It is not a different concept, from the WMI point of view. It is just our logic as to how it is implemented. In the previous example, we added property information as a row. Now all the property names should be the columns of the data table. We need to retrieve the instances of logical devices and present them in different rows of the same data table (separated with property values as column values). The following program example gives an idea of the approach.
Even though, the above program looks bit lengthy, it is very simple to understand. The next section is dedicated to a detailed discussion of the above program.
The program can be divided into three parts, for our convenience. The first part deals with preparing the structure (columns of properties) of the data table. The second part fills the data table with all of the information of logical disks. Finally, the third part displays the information back to the form.
The above fragment is almost similar to that of the first program we discussed. The main goal of the above fragment is to create columns of the data table. The columns should be the property names themselves. To retrieve the property names, we need to connect to at least one logical device (which would definitely be present). I am connecting to ‘c:’ in this case. Using the FOR loop, I am retrieving all property names and adding them as columns to the data table.
The following is the second part:
'populating with rows
DimsearcherAsNewManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk")
If you carefully observe the above fragment, we are using nested loops. The outer loop works on each logical disk, and the inner loop works with the properties of the current logical disk of the outer loop. This fragment is almost similar to the examples I provided in part three, except that I am using an inner loop. The inner loop actually assigns the information about all of the properties (of a particular disk) to a data row, which gets added to the data table.
And I hope you can understand the following third part very easily.
'displaying
dt.AcceptChanges()
Me.DataGrid1.DataSource = dt
The above just ensures that data table is filled. It finally gets displayed using a data grid.