I am running a VPS with only 50Gb disk space so have to regularly clear log files and such which, as an unimportant server only personal stuff, I wasn’t too fussed about automating. This VPS was purchased originally so I could code things and try them out online so I decided to look into building a tool to easily visualise the state of a Windows system.
There’s so much information about WMI that it can be difficult to know where and how to start so my first stop is to begin getting to grips with what exactly WMI is in terms of what the relationship is between it and the things it is telling you about and what the data model looks like.
WMI uses the Common Information Model (CIM) to interact with, and even control, system data. The Wikipedia article explains that this model is updated regularly and provides a helpful link to the Distributed Management Task Force site where the specifications are publically available. For example, this is the current (from June 2015) complete CIM specification: DMTF Schema Documentation 2.44.1.
At this point I now have the information available to know exactly what is possible through WMI but this is entirely different to what Windows will actually let me do. The next step is to decide on how I’m going to interact with WMI.
The MSDN has a helpful article which gives an overview of what providers can be used and how they work. From the WMI Architecture article I know that anything which can query COM will work and that .NET is by design the best choice.
It’s at this point where things get tricky because there are several ways of working with WMI and it seems you, as the programmer, kind of need to know all of them to get anything useful in .NET because you’re simply provided some objects like ManagementObject and ManagementClass and left to your own devices. There is no nice enum of every piece of information you might want to pull from WMI and while you can search online it’s very likely the ‘name’ of something that looks interesting simply won’t work through .NET. Also, the DMTF CIM scheme documentation gives you their name for everything but Microsoft obscures this by modifying their implementation of the scheme. For example, CIM_PROCESS is now WIN32_PROCESS. This does actually make sense in one way as CIM is a method of describing data and it can be used on far more than just Windows but Microsoft want to give their users only information relevant to Windows and, of course, put their own spin on things.
Thankfully Microsoft have listed all of their WMI classes on the MSDN and that is exactly what I’ve been searching for: Performance Counter Classes.
using System; using System.Windows.Forms; using System.Management; namespace WMITesting { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { ManagementClass mc = new ManagementClass("Win32_PerfFormattedData_PerfOS_Processor"); mc.Get(); foreach (var p in mc.Properties) textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value)); } } }
This works in as much as it gives some information back to me but, oddly, the collection it returns does not contain any values.
As it turns out, all this code does is create a new instsance of Win32_PerfFormattedData_PerfOS_Processor instead of requesting a proper one from the system. Getting it to actually get any values from the system is confusing at first and I learned that WQL is used for ManagementObjectSearcher while a ‘standard’ ManagementPath will use something like \\.\root\CIMV2: Win32_LogicalDisk.DeviceID=”C:” as the example shows in MSDN. I know what information I want so I don’t want to do a search every time the application updates so I need to find how to get a current instance of Win32_PerfFormattedData_PerfOS_Processor which means finding what that ‘path’ should be. Clearly the application needs to be told where to look to get the data and this is done using ManagementScope.
private void Form1_Load(object sender, EventArgs e) { ManagementScope wmiScope = new ManagementScope(@"\\.\root\cimv2"); ManagementPath wmiPath = new ManagementPath("Win32_PerfFormattedData_PerfOS_Processor.Name=\"_TOTAL\""); ObjectGetOptions wmiOptions = new ObjectGetOptions(null, TimeSpan.MaxValue, true); ManagementObject wmiObject = new ManagementObject(wmiScope, wmiPath, wmiOptions); wmiObject.Get(); foreach (var p in wmiObject.Properties) textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value)); }
What if I want more granular information instead of just looking at _TOTAL for the process class or C: for the disk. I might want to list everything that’s available and let the user decide. After a lot of playing around and trying to avoid using the searcher it became clear that ManagementObject is for single instances while ManagementClass lets you get all instances of the class.
private void Form1_Load(object sender, EventArgs e) { ManagementScope wmiScope = new ManagementScope(@"\\.\root\cimv2"); ManagementPath wmiPath = new ManagementPath("Win32_PerfFormattedData_PerfOS_Processor"); ObjectGetOptions wmiOptions = new ObjectGetOptions(null, TimeSpan.MaxValue, true); ManagementClass wmiClass = new ManagementClass(wmiScope, wmiPath, wmiOptions); var wmiInstances = wmiClass.GetInstances(); foreach (var i in wmiInstances) foreach(var p in i.Properties) textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value)); }
This code gives the same output as before but with data and includes every instance of the Win32_PerfFormattedData_PerfOS_Processor class.
That’s it for this post. I’ve gone from using WMI only through the command line to knowing where to find all the class names & their descriptions then pulling back individual or all instances of that class in any application I want. The next obvious step is to query remote computers but I’m thinking of looking at how to display the data in a different way and may do another post about it at some point.
Leave a Reply