This month, Doug Steele looks at how to tap into the wealth of information available through Windows Management Instrumentation (WMI), a component of the Microsoft Windows operating system.
I have some applications that can run from 30 minutes to 1.5 hours depending upon which computer and where the database is located. To benchmark my applications, I store the times it takes to run different parts of the application. What I’d like to do is get such computer information as the processor type and speed, the amount of RAM, and information about the Network Adapter. For example, I’d like to know: CPU Intel Pentium 4 CPU 2.80GHz, 1.00 GB of RAM, Network Adapter 100Mbps. Is this information even available?
Windows Management Instrumentation (WMI), a component of the Windows operating system that provides management information and control in an enterprise environment, provides a relatively easy way to get at that sort of information.
Originally released in 1998 as an add-on component with Windows NT 4.0 Service Pack 4, WMI is based on industry standards overseen by the Distributed Management Task Force (DMTF). It’s been described as “the instrumentation and plumbing through which all–well, almost all–Windows resources can be accessed, configured, managed, and monitored.” It’s also been described as “Microsoft’s best-kept secret.”
The purpose of WMI is to provide a standardized means of managing a computer system, where management really just means collecting data about the state of a managed object on the computer system and, potentially, altering the state of that managed object by changing the data stored about the object. A managed object can be a hardware entity (such as a memory array, a port, or a disk drive), or it can be a software entity (such as a service, a user account, or a page file).
A key component to understand when using WMI is that of managed objects and providers. A WMI managed object is a logical or physical enterprise component, such as a hard drive, network adapter, database system, operating system, process, or service. A managed object communicates with WMI through a WMI provider, which is a COM object that acts as an intermediary between WMI and a managed object. WMI providers supply WMI with data from a managed object, as well as handling messages from WMI to the managed object.
Typically, a provider will contain one or more classes, with each class having a mix of properties, methods, and events associated with it. At any point in time, the provider will manage one or more instances of the class associated with the managed object.
If anything, the biggest problem with WMI is that there’s too much information. For any piece of information, the “trick” is to know which class (or classes) contains the information of interest. The list of classes is immense, so I won’t list them all here (they’re well documented in MSDN; start at http://msdn.microsoft.com/library/en-us/wmisdk/wmi/wmi_classes.asp). However, I will give a representative overview.
The download includes a table that lists the categories of WMI classes. As mentioned in the table, most of the classes we’re interested in are probably in the Win32 Classes category, which are also listed in the download. The Computer System Hardware category breaks down even further (see the download for the subcategories).
Since you’re interested in knowing what sort of processor is in the machine, the Motherboard, Controller, and Port Classes subcategory would seem appropriate. That subcategory contains a large number of classes, which you’ll find in a table in the download as well.
Finally, the Win32_Processor class has a number of properties defined (again, this table is quite large, so I’ve put it in the download). You should also check out http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_processor.asp for more details on this class.
It’s good to know that the information is available–how do I get to it?
Let’s start with a simple case: the WMI class Win32_DiskDrive, which represents a physical disk drive as seen by a computer running the Windows operating system. There will be one instance of that class for each physical drive on your machine. Each instance would store information sent from the Win32 provider that shows information such as make and model, total size, and so on. (Note that the specific information that’s available depends on the actual hardware.) At the same time, there’s another WMI class Win32_DiskPartition that represents the capabilities and management capacity of a partitioned area of a physical disk on a Windows system.
The first step in any WMI script is to establish a connection to the Windows Management Service on the target computer. The simplest way of doing this is to call VBA’s GetObject function, passing the function the name of the WMI Scripting Library’s moniker (“winmgmts:”):
Dim objWMI As Object Set objWMI = GetObject("winmgmts:")
If you’re simply trying to connect to the local machine, that’s all you need. If you’re trying to connect to a remote machine, you need to include the name of the target computer as well:
Dim objWMI As Object Set objWMI = GetObject("winmgmts:\\machine1")
Connecting to WMI in this way returns a reference to a SWbemServices object, which is one of the dozen or so objects defined in the WMI Scripting Library. Once you have a reference to a SWbemServices object, you can call any of the methods available to SWbemServices. InstancesOf is one such method. As the method’s name suggests, InstancesOf returns all the instances of a managed resource identified by the resource’s class name. InstancesOf returns the requested resources in the form of a SWbemObjectSet collection, another scripting object defined in the WMI Scripting Library. To return all of the instances of Win32_Processor, you’d use:
Dim colItems As Object Set colItems = objWMI _ .InstancesOf("Win32_Processor")
The last and final step is enumerating the properties of interest in the SWbemObjectSet collection. Each item in a SWbemObjectSet is a SWbemObject (yet another object in the WMI Scripting Library) that represents a single instance of the requested resource. The following code illustrates how to retrieve the Name property associated with all Win32_Processor instances:
Sub GetProcessorInformation() Dim colItems As Object Dim objItem As Object Dim objWMI as Object Dim intCount As Integer Dim strOutput As String Set objWMI = GetObject("winmgmts:") Set colItems = objWMI _ .InstancesOf("Win32_Processor") strOutput = colItems.Count & _ IIf(colItems.Count = 1, _ " processor found:", _ " processors found:") & vbCrLf intCount = 0 For Each objItem In colItems intCount = intCount + 1 strOutput = strOutput & _ "Processor " & intCount & _ ": " & objItem.Name & vbCrLf Next objItem MsgBox strOutput, vbOkOnly + vbInformation End Sub
If I run that code on my laptop, I get a message box like the one shown in Figure 1.
That takes care of the disk drives. But how do I get to all of the other information that I want–the amount of memory, processors, and so on? Is there information that I can’t get?
To satisfy the rest of the specific question asked at the start of this column, it turns out that you have to look at other classes. Before doing that, I’ll give you the bad news: Getting all of the requested information about the network connection doesn’t appear to be possible. While there is a Speed property for the Win32_NetworkAdapter class, its description is “This property has not been implemented yet. It returns a NULL value by default.” Consequently, all I’m actually doing in the accompanying database is returning a list of the various network adapters on the machine.
But it’s all good news after that. In fact, if you look at the database in this month’s download, you’ll see that I actually show three different techniques to determine the total RAM on the machine. One is to use the Win32_PhysicalMemory class. That class has one instance for each physical memory device (DIMM) in the computer. By adding together the Capacity property of each instance, you can determine the total memory. The other two are essentially the same method. The TotalPhysicalMemory property of the Win32_LogicalMemoryConfiguration class is described as returning the total amount of physical memory available to the operating system, in kilobytes. However, the documentation states that the property is no longer supported. In its place, you can use the TotalVisibleMemorySize property of the WIN32_OperatingSystem class.
In the code that follows, I show an alternative approach to returning the SWbemObjectSet collection: something called WMI Query Language (WQL). You can learn more about WQL at http://msdn.microsoft.com/library/en-us/wmisdk/wmi/querying_with_wql.asp. I also call a couple of helper functions (GetMachineName and FormatSize). Due to space limitations, you’ll have to look in the sample database to get their code.
Sub GetMachineInformation() Const wbemFlagReturnImmediately = &H10 Const wbemFlagForwardOnly = &H20 Dim strComputer As String Dim objWMIService As Object Dim colItems As Object Dim objItem As Object Dim curMemory As Currency Dim strOutput As String strComputer = GetMachineName() Set objWMIService = GetObject("winmgmts:\\" & _ strComputer & "\root\CIMV2") Set colItems = objWMIService.ExecQuery( _ "SELECT * FROM Win32_Processor", "WQL", _ wbemFlagReturnImmediately + _ wbemFlagForwardOnly) strOutput = "The following processors were " & _ "detected in this machine:" & vbCrLf For Each objItem In colItems strOutput = strOutput & Trim(objItem.Name) & vbCrLf Next objItem Set colItems = objWMIService.ExecQuery( _ "SELECT * FROM Win32_PhysicalMemory", _ "WQL", wbemFlagReturnImmediately + _ wbemFlagForwardOnly) curMemory = 0 For Each objItem In colItems curMemory = curMemory + objItem.Capacity Next objItem strOutput = strOutput & vbCrLf & _ "The total memory in this machine is " & _ FormatSize(curMemory) & " (" & _ curMemory & " bytes)" & vbCrLf Set colItems = objWMIService.ExecQuery( _ "SELECT * FROM Win32_OperatingSystem", _ "WQL", wbemFlagReturnImmediately + _ wbemFlagForwardOnly) For Each objItem In colItems strOutput = strOutput & _ "Total Visible Memory Size = " & _ objItem.totalVisibleMemorySize \ 1024 & _ " Mb (" & _ objItem.totalVisibleMemorySize & " Kb)" & _ vbCrLf Next objItem Set colItems = objWMIService.ExecQuery( _ "SELECT * FROM " & _ "Win32_LogicalMemoryConfiguration", "WQL", _ wbemFlagReturnImmediately + _ wbemFlagForwardOnly) For Each objItem In colItems strOutput = strOutput & _ "Total Physical Memory = " & _ objItem.totalPhysicalMemory \ 1024 & _ " Mb (" & objItem.totalPhysicalMemory & _ " Kb)" & vbCrLf & vbCrLf Next objItem Set colItems = objWMIService.ExecQuery( _ "SELECT * FROM Win32_NetworkAdapter", _ "WQL", wbemFlagReturnImmediately + _ wbemFlagForwardOnly) strOutput = strOutput & _ "The following network adapters were " & _ "detected on this machine: " & vbCrLf For Each objItem In colItems strOutput = strOutput & _ objItem.Description & _ IIf(Len(Trim(objItem.AdapterType & _ vbNullString)) > 0, _ " (" & objItem.AdapterType & ")", _ vbNullString) & vbCrLf Next objItem MsgBox strOutput, vbOKOnly Or vbInformation End Sub
Figure 2 shows the results of my routine GetMachineInformation for my laptop.
Wow. There’s a lot of information here. Is this everything?
Realistically, WMI is too broad an area for me to cover in any great depth in a single column. Hopefully, though, I’ve given you enough of an overview to pique your interest, and you can learn more about it on your own.
Should you want more information about WMI, the WMI Administrative Tools–available for free download at www.microsoft.com/downloads/details.aspx or by following the Windows Management Instrumentation (WMI) Tools link at
http://msdn.microsoft.com/downloads/list/wmi.asp –provides a lot of details.
As well, there’s a great little tool, Scriptomatic 2.0, that will write WMI scripts for you. While it’s intended for VBScript, the scripts are compatible with VBA. Check
www.microsoft.com/technet/scriptcenter/tools/scripto2.mspx to download this tool.
I’ve also found the following references to be useful:
Other Pages On This Site You Might Like To Read