The NT Insider

WMI - What it is...Why Driver Writers Should Care
(By: The NT Insider, Vol 5, Issue 3, May-Jun 1998 | Published: 15-Jun-98| Modified: 20-Aug-02)
 Click Here to Download: Code Associated With This Article, ZIP Archive, 18KB 

In our ongoing struggle to keep you informed of all the new additions to NT, we thought it was about time to discuss ?Windows Management Instrumentation?, also know as WMI.  What is WMI?  WMI is new interface for Windows 98, NT 5.0 and supposedly SP4 for NT4.0 (by the way, this is The NT-Insider, so we?ll only talk about NT here).  It is a kernel-level instrumentation technology for MS operating systems.  WMI is part of the ?Web-Based Enterprise Management? (WBEM ? or initiative which simplifies instrumentation and provides consistent and open access to management data.  In simple terms, it is a way for driver writers to return information, either data or events, to a management database. This management database can be analyzed to provide system managers with information on how to better manage, troubleshoot, and tune their systems.


In this article, we?ll talk about what WMI is all about, what a driver needs to do to support WMI, and how a driver writer creates a schema that defines the data to be tracked and events that can be logged by WMI.   We?ll then discuss the interfaces that your driver must use to interact with WMI.  Finally we?ll talk about WMILIB, a quick and easy interface to WMI (Well, it?s quick and easy if what your logging is quick and easy). 


Drivers that do not need the complete flexibility that is provided by writing directly to the WMI interface can use the easier WMILIB interface instead.  So as an added bonus, we have put a copy of our ?Nothing? driver on illustrating the use of WMILIB.


Before continuing, I should note one thing.  The documentation for WMI is still under development, and thus is prone to errors and omissions.   I have attempted to piece together all the information I could find on WMI into an article that will get you thinking about how you can use WMI in your driver to help you improve your product.


What WMI is All About


According to Microsoft, WMI publishes information, configures device settings, and supplies event notification from device drivers.  WMI is part of the WDM architecture, but any driver that is written for NT 5.0 can use the WMI API. The data distributed by WMI is shown in Figure 1.


  • Published data ? A standard set of WMI data that will be built into the Windows NT 5.0-supplied port/class drivers.
  • Custom data ? Provided through OEM/IHV driver extensions.
  • Secure data  - Provided through Windows NT security descriptors for a designated usage.
  • Expensive data (optional) ? Some data collection activity can significantly affect the performance of the driver.  This data should only be collected when the management application specifically requests it.  By default, a driver will not collect the expensive data.  When a management application using WBEM expresses interest in that expensive data, WMI signals the driver to start collecting the data.  WMI also keeps a reference count and signals the driver to stop collecting the data when the last WBEM application interested in that data terminates.  Fortunately, it is the driver writer, not WMI, decides what data is expensive to collect.  And the mechanism for indicating that data is expensive to collect is extremely simple.  For information, see the ?Adding WMI Code to a driver? white paper, which is available at 
  • Event Notifications ? Event notification is a key feature of WMI, allowing drivers to detect hardware events and errors.  An event can then be passed to WBEM for corrective action based on the specific event that has occurred.

Figure 1 ? Data Distributed By WMI


WMI also allows a management application to configure a device.  A management application may need to reconfigure a device based upon some driver-raised event or because of the data collected by the management application.  For example, let?s say that your NIC driver starts to encounter lots of errors on the network.  It could fire a WMI event that some management application monitoring events would notice.  This management application could be so kind as to notify the network manager via mail (how ironic, maybe mail doesn?t work because of the network), who would go out and manually fix the network problem.  However, the network management application could just as easily be written to have issued a WMI operation to the network driver to modify its? behavior, by changing one of the drivers settings through WMI, in order to correct the detected problem.


Figure 2, courtesy of Microsoft, displays the WMI/WBEM architecture.  Note that other drivers designed specifically for NT5.0 (or NT4.0 SP4 for that matter) can also use WMI.



Figure 2 ? WMI/WBEM Architecture (From Microsoft Corporation)


So you?re probably saying to yourself, why is this of any interest to me?  That?s what I said at first glance too, but then I started thinking about how I could use this API to my advantage. 


First of all, WMI allows the device driver writers to very easily instrument their driver with performance counters and easily get the data displayed via the new Perfmon (This bypasses the agony of creating a performance dll for the old Perfmon.  Those of you who have tried this will know what I am talking about).  Secondly, driver writers now have a way via WMI?s event interface to notify administrators of problems.   I don?t know about you, but I?ve always felt that nobody looked at the NT Event Log until it was too late.


What a Driver Needs to do to Support WMI


Any NT5.0 driver that processes IRPs can support WMI.  To start receiving WMI IRPs the driver writer must do the following:


1.        Call IoWMIRegistrationControl(?) to register with WMI.

2.        Support the IRP_MJ_SYSTEM_CONTROL dispatch entry point

3.        Manage instance names for the data blocks the driver provides

4.        Publish a schema to describe the data that the driver will be providing.


One item to remember is that even those device drivers that do not register with WMI to provide data must still forward WMI requests to the next device in the device stack.  Also, all WMI functions are called at IRQL PASSIVE_LEVEL.


Calling IoWMIRegistrationControl(?)


Drivers wishing to register with WMI must do so by using the IoWMIRegistrationControl(?) API.   This API takes as input the Device Object of the device and a WMIREGACTION parameter, which indicates the registration action that the driver is performing.  The registration actions that can be performed are:







At least the simple actions are self-explanatory, right?  We?ll talk more about this later.




Once a driver has registered with WMI, it will start receiving IRP_MJ_SYSTEM_CONTROL IRPs.  These IRPs received with IRQL PASSIVE_LEVEL describe the target of the WMI operation.  The parameters of the WMI operation are passed in the Parameters WMI structure of the current IRP stack location.  The format of this structure is shown in Figure 3.

Struct {
       UINT_PTR      ProviderId;
       PVOID         DataPath;
       ULONG         BufferSize;
       PVOID         Buffer;
} WMI;


Figure 3


The ProviderId field indicates the Device Object to which this IRP is targeted.  Which for some unknown reason is defined as a UINT_PTR instead of a PDEVICE_OBJECT.  If its contents do not match the address of your Device Object, then this request must be forwarded down to the next Device Object in the device Stack.  If there isn?t one, you should report an error.


If however, the ProviderId matches your Device Object, then the rest of the parameters in the WMI structure describe the incoming request.  The DataPath parameter is a pointer to a Globally Unique Identifier which indicates which block is the target of this operation (we?ll discuss GUIDs later).  The BufferSize parameter specifies the actual or maximum length of the input buffer, depending on the incoming request.  Finally the Buffer parameter is a pointer to the input and/or output buffer for the operation.  The buffer is always allocated from Non-Paged Pool.


Managing Instance Names


Instance names are used by management applications to associate the information returned in a data block with the component that generated the information.  WMI uses the instance name to determine to which component a request should be targeted.  Because of this it is necessary for the instance names of different data blocks be unique.   This is to prevent name collision Note that because WMI knows that the same instance names may come from different device drivers, it automatically detects a collision and does name mangling in some cases.


Your driver can register or update each data block it registers to have either static or dynamic instance names.  Static names are meant for data blocks whose number of instances or names change very infrequently. For example, if you had a device for which you created 3 device objects (let?s say Com1, Com2, and Com3), that stayed around all the time you would want to use static names.  On the other hand, if you have data to publish for objects that change frequently, for example, network connections, IP addresses, etc., you would use dynamic names.


WMI is optimized to handle static instance names.   Instead of passing full instance names on calls to your driver, WMI will pass indices.  The index indicates what instance name is referenced by the request. This saves both space and processing time.  On the other hand, dynamic instance names are more difficult to handle.  These require using the full-blown WMI to implement.


Publishing a Schema


WMI requires that a driver provide schema information, which describes the information to be published in MOF (Managed Object Format) syntax.  This schema is run through a new Microsoft supplied compiler called MOFcomp, which compiles the schema into a file that you add as a resource in the device driver file.  The name of the resource included in the device driver file is passed to WMI when the driver registers.  The first time a driver registers with WMI, WMI reads the MOF resource and installs it into the WBEM schema database.


Drivers only provide Globally Unique Identifier (GUIDs) for data that is unique to the driver registering.  Standard information that is provided by Microsoft is automatically added to the WBEM schema database by WMI and should not be provided in the driver?s schema definition.


GUIDs - What are They, Why are They Needed


A Globally unique identifier (GUID) identifies a particular object class and interface, or in the WMI driver writer?s case, identifies a particular set of data that the driver publishes. This identifier is a 128-bit value. GUIDs can be generated using Guidgen, a Windows-native program, which is included with Microsoft's Visual C++ products. Developers can also use uuidgen.exe, a console application, from the Win32 SDK.


MOF - Managed Object Format


A Managed Object Format (MOF) file contains an ASCII text description of classes and data to be added to the Common Information Model Object Manager (CIMOM) database. When a MOF file is submitted to the MOF compiler, the compiler parses the contents of the MOF file and makes calls to CIMON based on each parsed item. CIMOM responds to these calls and adds data to the CIMOM database file as required.  Once the compiler has been run on the file, it passes the output through another program called Wmimofck, which verifies that all class definitions are valid for WMI.   If the file does not pass this test, it is deleted.  Wmimofck is run automatically by the MOFComp compiler upon its successful compilation of your driver?s MOF file.


Just to reiterate what I mentioned previously: A WMI driver creates a MOF file to describe the data and events that it wants to publish via WMI.  The driver writer then runs the MOF file through the MOFComp compiler, which generates a file that is added as a resource to a drivers ?.rc? file. 


Having said that, here is a copy of the MOF file (Figure 4) we created as part of our OSR Sample Driver, which has been extended to support WMI.   As you will notice the file contains a GUID which uniquely identifies the data block.  Look over the MOF file and we?ll discuss the fields below.


Example of a MOF file (nothing.mof)

[Dynamic, Provider("WMIProv"),
 Description("NOTHING Driver statistics"),
class NothingStatistics
    [key, read]
     string InstanceName;
    [read] bool Active;
     Description("Number of bytes read"),
    sint64 BytesRead;
     Description("Number of bytes written"),
    sint64 BytesWritten;
     Description("Number of read operations"),
    uint32 ReadCount;
     Description("Number of write operations"),
    uint32 WriteCount;
[Dynamic, Provider("WMIProv"),
 Description("NOTHING Driver Event"),
class NothingEvent : WMIEvent
    [key, read]
     string InstanceName;
    [read] bool Active;
        uint32 OSREventValue; 
         uint32 OsrEventRate; 
         uint32 OSREventTrigger;

 Figure 4 ? MOF File (nothing.mof)


As you can see in this MOF example, a data block is defined as a class.  A class definition in MOF may have qualifiers as listed in Figure 5.





Indicates the GUID that represents this class.  This is a required parameter.


Description of the class or property in the locale specified in the Locale Qualifier.


Specifies how much system resources are required in order to collect data in the data block.  The Value is defined to be the average number of CPU cycles needed to collect the data block.  If the qualifier is not specified, the value is assumed to be 0.


This is a required parameter and indicates that the class is dynamic.


This is required and indicates that wmiprov.dll is the provider for this class.


This is required and indicates that this is a WMI class.


Specifies the locale to use for this class.

 Figure 5 ? Class Definitions

As for data definitions for MOF, they may have qualifiers as shown in Figure 6 (Notice the similarities to the definitions that a driver originally used to create performance data for Perfmon.).





Indicates that the following field is to be used as a property to allow consumers to select among different instances


This field denotes the type of counter.  Unfortunately, I could not find a definition for this field in the documentation.  However, I would suspect that this field is similar in purpose and definition to the PERF_TYPE_COUNTER field as defined in winperf.h


Specifies that the data item can be read.


Specifies that the data item may be written.

WmiDataId(data id)

Specifies the data id for the data item.   This parameter is required.


Specifies the scaling factor to use when displaying the data.  Before displaying the data returned from a query of the data it is multiplied by 10 to the power of the scale factor.  If this qualifier is not specified, the scaling factor is assumed to be 0.


Specifies that the data item is really a 64-bit time stamp.  The time stamp is in units of 100 nanoseconds since 1/1/1601 (just like SYSTEMTIME).  This is only valid for 64 bit data items.


This specifies the name of the data item that can be used to trigger the event.  In the MOF file above, the driver will fire this event when the value in ?OSREventTrigger? has been reached.  This qualifier is optional and may only be used in classes that inherited from WMIEvent.


This specifies the name of the data item that can be used to specify a rate for the event. This qualifier is optional and may only be used in classes that inherited from WMIEvent.


Specifies the level of detail associated with the counter.  The allowable values are ?Novice?, ?Advanced?, ?Expert?, and ?Wizard?.  This indicates what level the reader should be at in order to understand the information being provided by this value.


Specifies how often this data is updated internally.  The interval is defined in units of milliseconds.  If this qualifier is not specified, then no assumption on the length of validity can be made.


Specifies the property within the current class that has the count of the number of array elements (not bytes) contained in a variable length array.


 Figure 6 ? Data Definitiions

The data types for the items contained with the MOF file are allowed to be any of those shown in Figure 7.


Data Types

Data Format


Counted Unicode String


Signed 32-bit integer


Unsigned 32-bit integer


Signed 16 bit integer


Unsigned 16 bit integer


Signed 64-bit integer


Unsigned 64-bit integer


Signed 8-bit integer


Unsigned 8-bit integer


25-character string used to specify absolute dates or time intervals.  The format of the date is as follows:  yyyymmddhhmmss.mmmmmmsutc.  Where yyyy is a 4-digit year, mm is the month, dd is the day, hh is the hour (24-hour clock), mm is the minute, ss is the second and mmmmmm is the number of microseconds.  S is a ?+? or ?-? indicating the sign of the UTC correction field, and utc is the offset from the UTC in minutes.


Byte where zero is False, non-zero is TRUE.

Figure 7 ? Data Types

You will notice that the two classes, NothingStatistics and NothingEvent both have a class member named Active.   This member is not part of the data block provided by the driver, but is used by consumers of the WMI Information.  This field indicates whether or not WMI is able to return data from the data provider for the data block.  This is a required field for all class definitions.   Also notice that each class definition has an InstanceName member (which is also required for all class definitions).  This holds the instance name for the data block returned by the data provider.  It has to be defined as a key property so that data consumers can select among different instances of the data block.


The compilation of the driver?s MOF file is done through adding a MAKEFILE.INC to your driver directory.   This file (Figure 8) is called by the NT DDK build command as part of the build process.  The invocation of the ?MOFcomp? command will build the nothing.bmf file, which will need to be added as a resource in a drivers ?.rc? file.



Clean: cleanup MOF 
del nothing.bmf 
MOF: nothing.bmf 
nothing.bmf: nothing.mof
    mofcomp -B: nothing.bmf nothing.mof

 Figure 8 ?

A driver writer can include the compiled MOF file as part of their resource file by using the syntax contained in the nothing.rc resource file shown in Figure 9.  As you can see the generated nothing.bmf file is added as resource or type MOFDATA and it is named MofResource.  This name is important, because when registering your driver with WMI, you must tell WMI the name of the resource you adding so that it can ensure that schema you are adding is added to the WBEM database schema.


#include <windows.h>
#include <ntverp.h>
#define VER_FILEDESCRIPTION_STR                        "OSR SAMPLE Driver"
#define VER_INTERNALNAME_STR                    "nothing.sys"
#define VER_ORIGINALFILENAME_STR                "nothing.sys"
#include                                        "common.ver"

MofResource MOFDATA nothing.bmf

 Figure 9 ? nothing.rc

One thing I should mention here is that WBEM does not support versioning, so if you change the format of data that you are publishing, you must define a new GUID and a new MOF class name.


Driver Statistics


As I mentioned earlier WMI is a great way to instrument your driver in order to get statistical information on how your driver is performing.  However, collecting statistics sometimes affects the performance of your driver, so you may only want to gather them if someone is actively interested in the data.  By setting the WMIREG_FLAG_EXPENSIVE flag when registering your data block or by setting the WMIExpense option in for your class definition in your MOF file, you tell WMI that the data block being registered is expensive for your driver to collect. This tells WMI that you want to be notified when someone is interested in the data.  WMI will notify you when it is interested in your data by sending WMI IRPs (IRP_MJ_SYSTEM_CONTROL) to your driver with either a minor function code of IRP_MN_ENABLE_COLLECTION or IRP_MN_DISABLE_COLLECTION. Your driver does not need to keep track of how many people have registered or deregistered, WMI takes care of that for you, you only get one enable and one disable.


Driver Events


One thing that may not be obvious is that an ?event? is considered to be a query result pushed by a data provider to a data consumer that has registered to accept it.  Any defined data block can be an event because WMI makes no distinction between data blocks and event blocks (Not to be picky, but if there is no distinction between a data block and an event, then why do they have a WMIEvent class?).  The only guideline is that Events should be used for exceptional events during the operation of the driver, because there are a limited number of events that can be queued at one time.  What?s the limit, the documentation doesn?t say.


The WMI documentation indicates that drivers should be designed so that their events are off by default.   When a management application (also known as a WMI consumer) registers with WMI that it is interested in a particular event, WMI will send an IRP_MN_ENABLE_EVENTS IRP specifying the GUID of the event that the consumer is interested in.  When all consumers of the event have deregistered with WMI, your driver will receive an IRP_MN_DISABLE_EVENTS IRP specifying the GUID of the event to disable.  Your driver does not need to keep track of how many people have registered or deregistered, WMI takes care of that for you, you only get one enable and one disable.


As you can see in our Nothing.MOF File we have created an event NothingEvent.  This event contains 2 fields that WMI user mode agents can modify in order to control the event information being generated by our Nothing Sample driver, these fields being OSREventRate and OSREventTrigger.  In our driver the OSREventTrigger field contains some value that must be reached before the driver will generate a event, while the OSREventRate contains some value that indicates how often the driver will fire the event.




As I mentioned earlier WMILIB is the easy way to support WMI in a driver.  Its use is appropriate only for those devices that do not need the flexibility of dynamic instance names, which is provided through the use of the full-blown WMI interface, which I won?t take the time to describe in this article.  To use the WMILIB interface, the device driver must still register with IoWMIRegistrationControl(?) and provide an IRP_MJ_SYSTEM_CONTROL dispatch entry point handler.  But instead of parsing and handling all the nuances of every IRP_MN_XXX minor function, the driver writer calls IoWMISystemControl(?) passing to it a WMILIB_INFO structure, which it has previously initialized (either in it?s DriverEntry(?) or AddDevice(?) entry point).  This structure contains information about the GUIDs registered by the device driver, the device stack in which the driver resides, and defines callbacks for various WMI functions.  The beauty of WMILIB is that when called from your IRP_MJ_SYSTEM_CONTROL dispatch entry point, this routine process all WMI requests appropriately.  It looks at the incoming request and does the one of the following operations:


  1. Forwards requests down to underlying drivers, if the request is not for this driver;
  2. If the request is for this driver and the GUID specified is one supported by this driver, the driver specific routine registered in the WMILIB_INFO structure is called to handle the WMI request.

The Format of the WMILIB_INFO structure is defined in Figure 10.

typedef struct _WMILIB_INFO {
    // Next device lower in the stack
    PDEVICE_OBJECT LowerDeviceObject;
    // PDO associated with device
    // WMI data block guid registration info
    ULONG GuidCount;
    // WMI functionality callbacks
    PQUERY_WMI_REGINFO      QueryWmiRegInfo;
    PQUERY_WMI_DATABLOCK    QueryWmiDataBlock;
    PSET_WMI_DATABLOCK      SetWmiDataBlock;
    PSET_WMI_DATAITEM       SetWmiDataItem;
    PEXECUTE_WMI_METHOD     ExecuteWmiMethod;
    PWMI_FUNCTION_CONTROL   WmiFunctionControl; 

 Figure 10 ? WMILIB_INFO Structure

You?ll notice that the structure contains that address of the next lower driver in the device stack.  This is so IoWMISystemControl(?) (talked about in the next section), can pass the incoming IRP down to the next driver in the device stack if the IRP is not targeted for your Driver.


The LowerPDO field contains the address of the Lower Physical Device Object.   This is in most cases the address of the Physical Device Object that was passed to your drivers? AddDevice entry point (See the V5N2 issue of The NT Insider for more information about the AddDevice entry point, in NT5.0).


The GuidCount field contains a count of the number of GUIDs contained in the following GuidList field.  The GUIDs contained in this list are the GUIDs for all the data blocks and events that your driver supports.  If an incoming request destined for your driver contains a GUID that is not contained within this list, the request is rejected

Finally, the last fields of the WMILIB_INFO structure are the callbacks that your driver supports.  Only QueryWmiRegInfo and QueryWmiDataBlock have to be supported.  If a callback is set to NULL, then IoWMISystemControl(?) will return the appropriate error status.




This function is called from the IRP_MJ_SYSTEM_CONTROL dispatch entry point of your driver.  The format of this call is shown in Figure 11 with the parameter descriptions shown in Figure 12.

NTSTATUS  IoWMISystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp

 Figure 11 ? IoWMISystemControl






The WMILib information control block that was initialized by your driver.


A pointer to the device object for which this request is targeted.


Address of the IRP making the request

  Figure 12 ? IoWMISystemControl Parameter Descriptions

The caller passes to this function the address of the WMILIB_INFO structure that contains the callbacks and WMI information that your driver supports.   This routine when called, looks at the IRP and determines whether or not this Irp is for your driver.   If input IRP is for your driver and the request is for a GUID that your driver supports, IoWMISystemControl(..) calls the appropriate callback function in your driver as defined in the WMILIB_INFO structure.   If the call is not for your driver, it is forwarded on the next driver in the driver stack automagically.


IoWMI CompleteRequest(...)


When IoWMISystemControl(?) calls your drivers? callback function, calling IoWMICompleteRequest(?) performs the completion of the IRP in your callback function.  The format of this request is shown in Figure 13 and it?s parameter descriptions is Figure 14.


NTSTATUS IoWMICompleteRequest(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN NTSTATUS Status,
    IN ULONG BufferUsed,
    IN CCHAR PriorityBoost );

Figure 13 ? IoWMICompleteRequest





The WMILib information control block that was initialized by your driver.


A pointer to the device object for which this request is targeted.


Address of the IRP making the request


The return status code for the IRP.


The number of bytes needed to return the data requested in an operation.  In the case that the input buffer is too small, this will contain the number of bytes needed for the returned data.   If the buffer is large enough for the operation, then this will contain the number of valid bytes returned in the input buffer.


The priority boost given to the thread when the I/O operation is completed.

 Figure 14 ? IoWMICompleteRequest Parameter Descriptions

One thing to note here is that the status code that is returned from IoWMICompleteRequest(?) should also be the return status of your callback function.





The IoWMIFireEvent(?) routine gives the WMI driver writer the ability to notify the management application of some event that may need attention.  The event, is one of the events that you have defined in your MOF file.   The signature for this call is shown in Figure 15 and it?s parameter descriptions in Figure 16.


   IN PDEVICE_OBJECT DeviceObject,
   IN ULONG GuidIndex,
   IN ULONG EventDataSize,
   IN PVOID EventData);

Figure 15 ? IoWMIFireEvent





The WMILib information control block that was initialized by your driver.


A pointer to the device object for which this request is targeted.


Index to the GUID contained in the GUID list supplied in the WMILIB structure which specifies the Event being fired.


The number of bytes of data that are being sent with the event being fired.


The data that is part of the event being fired.


Figure 16 ? IoWMIFireEvent Parameter Description



In summary instrumenting your driver with WMI is a good thing to do.  Besides giving NT System Managers more information in order to diagnose problems in the system, it give you and the administrators performance information which can help you deliver a quality product to market.

This article was printed from OSR Online

Copyright 2017 OSR Open Systems Resources, Inc.