WMI objects are late-bound, and classes in the System.Management namespace
follow that design: all WMI classes and instances when represented in System.Management
are derived from System.Management.ManagementBaseObject
. This means that you can't access WMI object's members directly - you need to use, for example:
ManagementObject.Properties["Property Name"]
or shorter:
ManagementObject["Property Name"]
Both of these return an object of type System.Management.PropertyData
, so you can finally use:
PropertyData.Name
and:
PropertyData.Value
to access a WMI object property value. System.Management.PropertyData
also has some additional properties, like:
PropertyData.Type
PropertyData.IsLocal
PropertyData.IsArray
PropertyData.Qualifiers
that let you get additional information on the returned PropertyData
object. The last property in the above list returns an QualifierDataCollection
, a collection of WMI qualifiers for the property in question.
Here is an example of using System.Management namespace
to get Win32_Process
information:
using System;
using System.Management;
class Program
{
static void Main()
{
String className = "Win32_Process";
ManagementClass WMIClass =
new ManagementClass(className);
ManagementObjectCollection instances =
WMIClass.GetInstances();
foreach (ManagementObject instance in instances)
{
Console.WriteLine("Process Name:\t {0}",
instance.Properties["Name"].Value);
Console.WriteLine("Command Line:\t {0}\n",
instance["CommandLine"]);
}
}
}
This is not too bad, but it would be easier to access object members directly:
instance.CommandLine
rather than:
instance.Properties["CommandLine"].Value
The .NET Framework SDK helps with a command line tool called Management Strongly Typed Class Generator (mgmtclassgen.exe). Its basic syntax is:
mgmtclassgen <WMI class name> /P output file path
For example:
mgmtclassgen Win32_Process /P C:\Win32_Process.cs
When you execute this command, a C# source file named Win32_Process.cs is created. (You can use /l
command line option to generate code in C# (CS), Visual Basic (VB), Managed C++ (MC), Visual J# (VJ) and JScript (JS). You can include the generated file in your Visual Studio project (assuming you are using Visual Studio):
Project -> Add Existing Item... -> C:\Win32_Process.cs
Now, if you open Win32_Process.cs in Solution Explorer, you will see a lengthy code, starting with:
namespace ROOT.CIMV2.Win32 {
using System;
using System.ComponentModel;
using System.Management;
using System.Collections;
...
public class Process : System.ComponentModel.Component {
...
This means that mgmtclassgen
puts Win32_Process
class in ROOT.CIMV2.WIN32 namespace
(you can change this using the /o mgmtclassgen.exe
command line parameter), which also means you need to add a using
directive for that namespace
in Program.cs. The created C# class name is Process
:
using System;
using System.Management;
using ROOT.CIMV2.Win32;
class Program
{
static void Main()
{
foreach (Process win32Process in Process.GetInstances())
{
Console.WriteLine(win32Process.Name);
Console.WriteLine(win32Process.CommandLine + "\n");
}
}
}
This is shorter than the first sample, it has cleaner syntax, and Intellisense also works.
Calling WMI class methods from System.Management
is not ‘intuitive’ - you need to use indirect method invocation. This means that you first need to get method input parameter information with ManagementClass.GetMethodParameters()
, assign each parameter the appropriate value and pass it to ManagementClass.InvokeMethod()
along with the method name. ManagementClass.invokeMethod()
returns a ManagementBaseObject
that represents output parameters, which you also need to access indirectly. Here is an example:
ManagementBaseObject inParams = null;
ManagementClass classObj =
new ManagementClass(null, "Win32_Process", null);
inParams = classObj.GetMethodParameters("Create");
inParams["CommandLine"] = "Notepad.exe";
ManagementBaseObject outParams =
classObj.InvokeMethod("Create", inParams, null);
Console.WriteLine(outParams.Properties["ProcessId"].Value);
Compare this to the single line of code needed to call a method using MgmtClassGen.exe generated Process
class:
using System;
using System.Management;
using ROOT.CIMV2.Win32;
class Program
{
static void Main()
{
uint handle;
Process.Create("Notepad.Exe", null, null, out handle);
Console.WriteLine(handle.ToString());
}
}
If you look at the Process.Create()
definition in the generated class code, you can see that it does the same thing, indirect method invocation, behind the scenes just as the first piece of code. Only, in this case, the complexity is hidden in a separate class.
Most of the time, mgmtclassgen.exe works perfectly, well, it works perfectly if the generated class originates from Root\Cimv2 WMI namespace
OR it doesn’t have any static
(class
) methods. If you want to use a class from other namespace
AND has static
methods, like the StdRegProv
WMI class (used for accessing the Registry), everything crashes:
using System;
using System.Management;
using ROOT.DEFAULT;
class Program
{
static void Main(string[] args)
{
const uint HKLM = 0x80000002;
string subKeyName = "Software";
string[] keyNames;
Stdregprov.EnumKey(HKLM, subKeyName, out keyNames);
foreach (string keyName in keyNames)
{
Console.WriteLine(keyName);
}
}
}
MgmtClassgen.exe is used for generating a strongly typed class for StdRegProv
WMI class. MgmtClassGen
doesn’t report any errors, but the above simple code to list HKLM\Software subkeys returns an exception: "Not found
". If you look at the StdRegProv.cs code to find its EnumKey()
method, this is what you find (a bit reformatted):
public static uint EnumKey(uint hDefKey,
string sSubKeyName, out string[] sNames) {
bool IsMethodStatic = true;
if ((IsMethodStatic == true)) {
System.Management.ManagementBaseObject inParams = null;
System.Management.ManagementPath mgmtPath =
new System.Management.ManagementPath(CreatedClassName);
System.Management.ManagementClass classObj =
new System.Management.ManagementClass(
statMgmtScope, mgmtPath, null);
inParams = classObj.GetMethodParameters("EnumKey");
inParams["hDefKey"] = ((System.UInt32 )(hDefKey));
inParams["sSubKeyName"] = ((System.String )(sSubKeyName));
System.Management.ManagementBaseObject outParams =
classObj.InvokeMethod("EnumKey", inParams, null);
sNames = ((string[])(outParams.Properties["sNames"].Value));
return System.Convert.ToUInt32(
outParams.Properties["ReturnValue"].Value);
}
else {
sNames = null;
return System.Convert.ToUInt32(0);
}
}
ManagementClass
constructor accepts statMsgScope
parameter, which should hold a reference to ManagementScope
that points to the Root\Default WMI namespace
where the StdRegProv
class is located. Only it doesn't (at the beginning of StdRegProv.cs):
private static System.Management.ManagementScope statMgmtScope = null;
With this declaration, statMgmtScope
defaults to Root\Cimv2 - no wonder the method call throws the "Not found
" exception - there is no StdRegrov
class in Root\Cimv2 namespace
. So, finally, to get this to work, you need to change the above declaration to:
private static System.Management.ManagementScope statMgmtScope =
new ManagementScope("root\\default");
Mgmtclassgen.exe itself uses the GetStronglyTypedClassCode()
method of ManagementClass
for generating code. There is also a tool called the Server Explorer Management extension that is capable of generating strongly typed WMI classes. It is supposed to integrate with Visual Studio (the download page says Visual Studio .NET 2003, and I’m not sure if the extension exists for other VS versions).
Links relevant to this post: