WMI Provider Extensions in .NET 3.5 – Introduction and Read-Only Provider

April 30, 2008

tags:
no comments

Windows Management Instrumentation (WMI) is a C&C infrastructure that is integrated within Windows. It provides three primary capabilities:

  • Exposing state information regarding a configurable entity
  • Invoking control methods on a configurable entity
  • Publishing events from a configurable entity

These facilities are a complete instrumentation solution for any Windows application, and multiple system components expose information through the use of WMI providers. This information can be consumed from a multitude of languages and technologies by WMI consumers, using a standard query language (WQL).

Managed code support for WMI providers has been extended in .NET 3.5, enabling every capability a native WMI provider can support. This functionality resides in the System.Management and System.Management.Instrumentation assemblies, and is generally referred to as the WMI Provider Extensions for .NET.

The primary advantage to using WMI in favor of the multitude of communication technologies that are abound is that WMI is a standardized C&C mechanism which can be consumed by numerous existing C&C frameworks. Another advantage is that most Windows components expose C&C information using WMI, and it is preferable that a single C&C framework is used instead of reinventing a C&C framework for each individual component. This makes a single C&C tool suitable for a variety of configurable and controllable entities.

There is a significant number of resources demonstrating what kind of tasks can be accomplished using WMI: WMI Tasks for Scripts and Applications, WMI and .NET in the MSDN Magazine, WMI Code Creator Spotlight . . .  However, there is no single resource outlining the exact details of creating every kind of provider supported in the WMI Provider Extensions for managed code.

This series of posts provides guidance and walkthrough steps for implementing a WMI provider in managed code and consuming the information exposed by that provider from managed code.

Hosting Model

WMI providers can be hosted in a separate application, or within the WMI host service. Separately hosted providers are also called decoupled providers. Providers hosted within the WMI host service are also called in-process providers.

Decoupled providers expose their information only as long as the application in which they are hosted is running. In-process providers expose their information as long as the WMI infrastructure on the machine is functioning properly.

Basic Setup Steps

An assembly that contains a WMI provider must be decorated with the [WmiConfiguration] attribute, specifying the WMI namespace for the provider and the hosting model the provider chooses to use.

The following code demonstrates setting up an assembly that exposes a WMI provider in the “root\MyApplication” namespace, with a decoupled hosting model:

[assembly: WmiConfiguration(@"root\MyApplication",

    HostingModel = ManagementHostingModel.Decoupled)]

The following code demonstrates setting up an assembly that exposes a WMI provider in the “root\MyApplication” namespace, with an in-process hosting model requiring the provider to run under the NetworkService account:

[assembly: WmiConfiguration(@"root\MyApplication",

    HostingModel = ManagementHostingModel.NetworkService)]

To inform the WMI infrastructure that the assembly contains a WMI provider, the assembly must contain an installer class that can be run by the InstallUtil.exe tool. This installer class must derive from the DefaultManagementInstaller class and be decorated with the [RunInstaller] attribute. It does not require any additional functionality.

The following code demonstrates the installer class that must be present in the WMI provider’s assembly:

[RunInstaller(true)]

public class MyApplicationManagementInstaller :

    DefaultManagementInstaller { }

With these setup steps in place, all that is left is to implement the actual WMI provider (described in the subsequent sections). Once the assembly is complete, it must be registered using the InstallUtil.exe tool. From a Visual Studio command prompt, the following command registers the assembly with the WMI infrastructure:

InstallUtil MyAssembly.dll

To unregister the assembly, the following command can be executed from a Visual Studio command prompt:

InstallUtil /u MyAssembly.dll

Read-Only Information

The most rudimentary kind of WMI provider is a WMI provider that exposes read-only information regarding a configurable entity.

The managed model for this kind of WMI provider consists of implementing a class that exposes the information as read-only properties. The class itself must be decorated with the [ManagementEntity] attribute, and the properties must be decorated with the [ManagementProbe] attribute.

When applying the [ManagementEntity] attribute, you can choose whether the WMI class you are exposing will be a singleton entity, or whether it can have multiple instances. If your WMI class can have multiple instances, one or more of the read-only properties that you provide must be decorated with the [ManagementKey] attribute. These properties will allow WMI consumers to determine a consistent identity for the instances of your class that they are querying.

A multi-instance WMI provider which does not expose at least one property decorated with the [ManagementKey] attribute will not function properly at runtime. Any attempt to access the provider will result in an ExecutionEngineExecption in the hosting process. The exception details can be examined in the Application Event Log.

A singleton WMI provider which attempts to expose a property decorated with the [ManagementKey] attribute will fail to register during the installation phase.

Finally, the constructor of your class or a static method on your class must be decorated with the [ManagementBind] attribute. This specifies that the creation of an object instances when requested by the WMI infrastructure will be directed at the constructor or static method, respectively.

The following code demonstrates a singleton WMI class that exposes the number of processors available on the machine as a read-only property.

[ManagementEntity(Singleton=true)]

[ManagementQualifier("Description",

    Value="Obtain processor information.")]

public class ProcessorInformation

{

 

    [ManagementBind]

    public ProcessorInformation()

    {

    }

    [ManagementProbe]

    [ManagementQualifier("Descriiption",

        Value="The number of processors.")]

    public int ProcessorCount

    {

        get { return Environment.ProcessorCount; }

    }

}

To publish the instance to the WMI infrastructure, the hosting process can use the following code:

InstrumentationManager.Publish(new ProcessorInformation());

This simple class can now be registered using InstallUtil.exe and then queried from a WMI consumer. Note the use of the [ManagementQualifier] attribute to add description information to the class and property levels.

The following code demonstrates a multi-instance WMI class that exposes the thread count in each process running on the system as a read-only property.

[ManagementEntity]

public class ProcessInformation

{

    Process _theProcess;

    [ManagementBind]

    public ProcessInformation(

        [ManagementName("Id")] int id)

    {

        _theProcess = Process.GetProcessById(id);

    }

    [ManagementKey]

    public int Id

    {

        get { return _theProcess.Id; }

    }

    [ManagementProbe]

    public int ThreadCount

    {

        get { return _theProcess.Threads.Count; }

    }

}

To publish the process instances to the WMI infrastructure, the hosting process can use the following code:

foreach (Process p in Process.GetProcesses())

{

    InstrumentationManager.Publish(

           new ProcessInformation(p.Id));

}

Changes to object instances made by InstrumentationManager.Publish (publishing an object instance) or by InstrumentationManager.Revoke (revoking an object instance) are often not effectively immediately, but with a delay of a few seconds. When consuming WMI instances, a retry or timeout mechanism should be implemented when the object is initially published to alleviate this delay.

Consuming the information exposed by the provider developed in the previous section can be performed using the ManagementObjectSearcher, ManagementObject and other types. Additionally, there are numerous tools to aid in the development and testing of WMI providers, such as wmic built-in tool, or the GUI WMI Tools package. The Visual Studio Server Explorer can also generate a managed class wrapping a WMI provider.

Opening an administrator command prompt and executing the following command will yield a detailed list of ProcessInformation instances defined in the previous section.

wmic /namespace:\\root\MyApplication path ProcessInformation

The wmic tool supports query syntax for specifying conditions, as well:

wmic /namespace:\\root\MyApplication path ProcessInformation where Id=4

Alternatively, the following managed code can output the ProcessInformation objects:

ManagementObjectSearcher searcher =

    new ManagementObjectSearcher(@"root\MyApplication",

        "SELECT * FROM ProcessInformation");

foreach (ManagementObject obj in searcher.Get())

    Console.WriteLine("Id: " + obj["Id"] +

        ", ThreadCount: " + obj["ThreadCount"]);

If you’re looking for a strongly-typed accessor, there’s no need to generate it manually. Within Visual Studio’s Server Explorer, expand your server’s node, right click the Management Classes node and choose Add Classes. Navigate to the root\MyApplication namespace and choose the ProcessInformation class, click Add and dismiss the dialog. Now right-click the ProcessInformation node and select Generate Managed Class. Visual Studio automatically adds to your project a Component-derived class that enables strongly-typed access to the WMI provider. Now the following code can replace the ManagementObjectSearcher-based approach:

foreach (ROOT.MYAPPLICATION.ProcessInformation info in

    ROOT.MYAPPLICATION.ProcessInformation.GetInstances())

{

    Console.WriteLine("Id: " + info.Id +

        ", ThreadCount: " + info.ThreadCount);

}

Note that the underlying accessor still uses the weakly-typed ManagementObject, so using the accessor does not provide performance benefits – only correctness and ease of use. Throughout the rest of this series, the weakly-typed approach will not be demonstrated at all for brevity purposes.

In the next post, we will look into making our provider more interesting by exposing configurable (read-write) data and methods.  In the final post of this series we will also add support for publishing events and outline some advanced topics.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*