Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Mgmtclassgen.exe Issue

0.00/5 (No votes)
28 Apr 2010 1  
A Mgmtclassgen.exe Issue

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";

        // Get Win32_Process class

        ManagementClass WMIClass =
            new ManagementClass(className);

        // Get Win32_Process instances

        ManagementObjectCollection instances =
            WMIClass.GetInstances();

        // Loop through the instances 
        // and print some properties

        foreach (ManagementObject instance in instances)
        {
            // Use ManagementObject.Properties["Property Name"]

            Console.WriteLine("Process Name:\t {0}",
                instance.Properties["Name"].Value);

            // Use ManagementObject["Property Name"]

            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;

// use the class created by mgmtclassgen.exe

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;

// use the class created by mgmtclassgen.exe

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;

// Use the MgmtClassGen.exe generated class:

using ROOT.DEFAULT;

class Program
{
    static void Main(string[] args)
    {
        // Need this for the method call:

        const uint HKLM = 0x80000002;
        string subKeyName = "Software";

        // This will hold the returned subkeys

        string[] keyNames;

        // Call the method and print the results:

        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:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here