microsoft.public.win32.programmer.wmi
WMI programming from Win32.
The native-code twin of the .NET WMI group: MOF files, provider development, WQL from C++ and scripting hosts, and the diagnostic folklore WMI work generates.
System administrators and driver developers shared this room, to both sides’ occasional surprise.
On this page
- A room filed under the API, not the technology
- The COM route in
- Three ways to wait
- The interface catalogue
- MOF: the language the schema is written in
- Mofcomp, and what compiling actually did
- Writing a provider: the models and the registration
- Hosting, threading and the obligations of a long-lived object
- Why writing one was regarded as difficult
- The standard underneath
- The kernel side
- Why the room mixed two trades
- The group's own record
- What the record does not settle
- Scope and limits of this page
A room filed under the API, not the technology
The Internet Systems Consortium's mirrored copy of the Usenet active file still carries this group, and what the standard record says about it is almost nothing. The companion newsgroups file — the descriptive list from which news software builds a browsable group listing — carries no microsoft.* lines at all, so not even a one-sentence description of this room survives in the standard record. What the active file does establish is the shape of the branch: microsoft.public.win32.programmer.wmi is one of twenty-four groups beneath microsoft.public.win32.programmer. The siblings are the Win32 API areas of the late 1990s — kernel, gdi, ole, tapi and tapi.beta, messaging, networks, mmedia, international, rtc, ui, tools and wince — together with ten rooms for DirectX. Windows Management Instrumentation was filed among them not as a management technology but as another set of headers a C++ programmer had to include.
That filing is the subject of this page. WMI is Microsoft's implementation of an industry data model for describing manageable things; what it is, how its repository and providers fit together, and what the managed wrapper over it looked like are set out on the page for microsoft.public.dotnet.framework.wmi, and WQL — the SQL-shaped query language clients used to ask for instances and subscribe to events — belongs with the administrators' room at microsoft.public.windowsxp.wmi. What belonged here was the other side of the same object model: the COM interfaces a native application called to reach the service, the Managed Object Format files that put class definitions into the repository in the first place, and the writing of the providers that answered for those classes — including providers that lived inside device drivers.

Naming by catalogue rather than by subject was a property of the whole hierarchy, described on the microsoft.public.* page, which notes that WMI accordingly received three separate rooms under three different parents. The active file records a fourth. microsoft.public.wmi.programmer sits directly beneath microsoft.public, a room named for the technology rather than for one of the doors onto it; no description of it survives either. How its traffic compared with this group's is not something the surviving record settles, and this page does not guess.
The distinction the namespace was drawing is real enough to state plainly. From a scripting host, a WMI question was three moves and a loop. From C++ it was the same three moves wrapped in the apparatus of an out-of-process COM call, and it is that apparatus, rather than WMI itself, that generated most of what a native-code room had to explain.
The COM route in
WMI is a COM service, and a native client reached it the way a native client reached any out-of-process COM server: ceremony first, work afterwards. Microsoft's own procedure for building a WMI application in C++ runs to five numbered steps, of which only the fourth is the program's actual purpose. Initialise COM. Create a connection to a namespace. Set the security levels on that connection. Implement the purpose of the application. Clean up and shut down, destroying every COM pointer in order.
Step one is two calls. CoInitializeEx starts COM for the thread; CoInitializeSecurity sets the default process security level, which is the setting that decides how much authority another process must have to reach into this one. The documentation's standard argument list for a WMI client sets the authentication level to RPC_C_AUTHN_LEVEL_DEFAULT, so that DCOM negotiates whatever the target computer demands, and the impersonation level to RPC_C_IMP_LEVEL_IMPERSONATE. The declarations came from wbemidl.h, the interface identifiers from the import library wbemuuid.lib, and _WIN32_DCOM had to be defined before any of it would compile.
Step two is the connection. A call to CoCreateInstance on CLSID_WbemLocator, asking for IID_IWbemLocator, produces the one interface that exists to find WMI: IWbemLocator, whose single useful method, ConnectServer, takes a namespace path, an optional user name and password, a locale, security flags, an authority and a context object, and hands back a proxy to IWbemServices. That proxy is the client's entire relationship with WMI. Every subsequent operation — fetching an object, enumerating a class, running a method, executing a query, subscribing to events — is a method on it.
Step three is the step newcomers omitted. The pointer returned by ConnectServer is a proxy to an object in another process, and COM will not, by default, let one process act on another's behalf. Until CoSetProxyBlanket has been called on that proxy, the connection exists but carries no usable identity. The documented common form sets RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, an authentication level of RPC_C_AUTHN_LEVEL_CALL, an impersonation level of RPC_C_IMP_LEVEL_IMPERSONATE and no special capabilities. Two security calls at two different scopes — one for the process, one for the proxy — before a single class name has been mentioned. The Windows security model those constants belong to had a room of its own at microsoft.public.platformsdk.security, and the two subjects are difficult to keep apart.
Only then does the program do its work, and the shape of that work is fixed by the interfaces. A query submitted through IWbemServices::ExecQuery returns an IEnumWbemClassObject, a COM enumerator in the ordinary style; each call to its Next method yields one IWbemClassObject, which is the container for both class definitions and instances; each property is read from that object by name into a VARIANT, with a separate output telling the caller what CIM type the variant is standing in for. Strings crossing the boundary are BSTRs, allocated and freed by the caller. Out parameters are set to null before the call throughout the reference's own examples. Every call returns an HRESULT that has to be tested.
This is the answer to the question a native-code WMI room could not avoid: why the same task took so much more code from C++ than from a script. Nothing in the list above is exotic, and none of it is WMI's invention. It is simply that a scripting host performed all of it invisibly — apartment initialisation, security negotiation, reference counting, string allocation, variant unpacking, error checking — and a C++ program performed all of it in the open, in the right order, with a cleanup path for each failure. What the C++ caller bought in exchange was control: the flags, the context objects, the callback sinks, the ability to be a provider as well as a consumer, and, in the case of driver work, the ability to be on the other end entirely. Microsoft's own guidance put it in about those terms, recommending scripting for simple processes and C++ for sophisticated applications, and noting that C++ was required for writing providers. The documentation also recorded the tooling assumption of the period: WMI supported C++ development using Microsoft Visual C++ version 6.0 and later development systems, adding that other compilers — those from Borland and Watcom are the ones it names — could also be used.
Three ways to wait
A management call can take an arbitrary length of time, because somewhere at the end of it a provider may be interrogating hardware, a registry hive or a machine in another building. WMI therefore offered three calling styles, and choosing between them was a recurring subject for anyone writing native code.
Synchronous calls block the calling thread until the operation completes. They are the simplest to write and the reason a management utility can sit unresponsive while a provider works. Asynchronous calls hand WMI an object of the caller's own, implementing IWbemObjectSink, into which results are delivered by calls to Indicate as they arrive and terminated by a call to SetStatus. They are the most responsive and much the most work: the documentation is explicit that asynchronous code in C++ must implement the sink, use multiple threads and control the flow of information back to the caller, and that large result sets can consume significant resources being delivered.
Semisynchronous calls are the compromise Microsoft recommended. The client makes the call with WBEM_FLAG_RETURN_IMMEDIATELY set, usually together with WBEM_FLAG_FORWARD_ONLY where an enumerator is expected, and the method returns at once. For calls that produce an enumerator, results are then pulled with Next or the non-blocking NextAsync; for calls that do not, the client receives an IWbemCallResult object and polls GetCallStatus until the operation has finished. Releasing an enumerator early cancels delivery of everything still outstanding, which is the polite way to abandon a query that has already produced the one instance the program wanted.
The asynchronous route dragged its own security question behind it, since a callback is an inbound call into the client's process and has to be authorised as one. WMI shipped IUnsecuredApartment precisely to simplify making asynchronous calls from a client process, and the surrounding documentation on securing an asynchronous call is one of the more forbidding corners of the reference. The practical consequence is the familiar one: event-notification code that works on the machine it was written on, under the account it was written under, and stops working when either changes.
The interface catalogue
The COM API for WMI is small enough to list and large enough that knowing which interface answered which question was itself expertise. The principal members, as Microsoft's reference groups them:
- IWbemLocator — obtains the initial pointer to IWbemServices for a namespace on a given computer. The way in, and nothing else.
- IWbemServices — the primary interface, used by clients to access WMI services and implemented by WMI and by providers alike. Providers implement the small subset of it that they support and, as the documentation directs, stub the rest with
WBEM_E_PROVIDER_NOT_CAPABLE. - IWbemClassObject — contains and manipulates both class definitions and instances. Client code never implements it; WMI supplies the implementation.
- IEnumWbemClassObject — the enumerator over those objects, deliberately similar to a standard COM enumerator.
- IWbemObjectSink — the callback interface that receives both the results of asynchronous operations and event notifications.
- IWbemCallResult — the status object returned by semisynchronous calls.
- IWbemContext — carries additional context information into a call, and, as the provider section below explains, must be passed back out again.
- IWbemQualifierSet — the container for the named qualifiers attached to a class, an instance or a single property.
- IWbemRefresher, IWbemConfigureRefresher, IWbemHiPerfEnum and IWbemObjectAccess — the refresher machinery, for repeatedly re-reading the same objects at speed rather than re-querying for them.
- IWbemStatusCodeText — turns an error code into a text description, a service the reader of any WMI error message will recognise as necessary.
- IMofCompiler — the COM interface used by the MOF compiler and by anything else that needs to compile MOF, implemented by
Mofd.dll. - IWbemProviderInit, IWbemProviderInitSink, IWbemEventProvider, IWbemEventConsumerProvider, IWbemPropertyProvider, IWbemHiPerfProvider, IWbemDecoupledRegistrar and their neighbours — the provider side, which the rest of this page is largely about.
Two features of that list explain a good deal of what native-code WMI work involved. The first is the symmetry: IWbemServices is the interface a client calls and also the interface a provider implements, so the same method names mean subtly different things depending on which side of the service you are standing. Microsoft's own reference is not quite consistent about this — the interface page describes IWbemServices as implemented by WMI and by WMI providers, while the summary table on the COM API overview still describes it as implemented only by WMI — and the provider topics side with the former. The second feature is the density of return codes. The constants are defined in Wbemcli.h, they are numerous, and an unfamiliar one arriving in the middle of an operation told the recipient very little without a reference to hand, which is why IWbemStatusCodeText exists at all.
MOF: the language the schema is written in
Managed Object Format is the language in which CIM classes are described, and it is not Microsoft's language. The driver documentation states its parentage flatly: MOF is a compiled language created by the Desktop Management Task Force and based on Interface Definition Language. A person reading a .mof file for the first time who thinks it looks like IDL with square brackets is reading it correctly.
What a .mof file declares is a schema and, usually, a registration. Class definitions carry properties with CIM data types and methods with typed parameters; instances may be declared inline; and both classes and their members can be preceded by qualifiers in square brackets, which are the metadata the whole system runs on. Every class must have at least one key property, and no instance may be created with more than 256 keys. A derived class inherits from its base, which is how the Windows-specific classes sit on top of the vendor-neutral ones: the class describing a CD-ROM drive on a Windows system derives from the CIM class describing CD-ROM drives in general and adds what is peculiar to Windows.
The qualifiers are where a class definition stops being a description and starts being a wiring diagram. [key] marks the properties that identify an instance. [association] marks a class that relates two others. [implemented, static] marks a method the provider will actually execute. And the pair that matters most to a provider author is [dynamic] together with provider("Name"): the first says the instances of this class are produced on demand rather than stored in the repository, and the second names the provider that will produce them — a name that must match, exactly, the Name property of a registered provider instance in the same namespace. That string is the join between a schema and a DLL, and a mistyped one produces a class that exists, compiles, enumerates and returns nothing.
A MOF file also carries preprocessor directives. #pragma namespace states which namespace the definitions belong in; #pragma autorecover asks that the file be recompiled if the repository is ever rebuilt; #pragma deleteclass removes definitions. One name-space rule catches everyone once: WMI's own system classes are named with a leading double underscore, they are created by WMI in every namespace rather than declared in any MOF file, and the compiler will refuse to compile any class whose name begins with a double underscore, because that prefix is reserved.
MOF also marks one of the places where Microsoft's implementation went beyond the model it implements. Two objects can be related either by an association class or by embedding one object inside the other, and Microsoft's own design guidance says the quiet part out loud: CIM does not support embedded objects, so to be CIM-compliant you must use associations — but WMI supports embedding, and the Windows classes use it, a security descriptor containing access-control entries containing trustees. Anyone who has unpacked a nested object out of a WMI property has met the consequence of that sentence.
Mofcomp, and what compiling actually did
The compiler is Mofcomp.exe, and it lives, with the rest of WMI, in %Windir%\System32\wbem. It parses a file of MOF statements and adds the classes and instances it finds to the WMI repository. Most MOF on a Windows machine is compiled automatically during setup, from the .mof files shipped in that same directory; a developer ran the compiler by hand, repeatedly, and its documented behaviour repays setting out in full.
The switches describe the job better than any summary. -check performs a syntax check only, establishes no connection to WMI and modifies nothing. -N: names the namespace to load into — without it, and without a #pragma namespace in the file, everything lands in root\default, which is almost never where a Windows class was wanted, and a remote target could be given as a machine path. The class switches govern collisions: -class:createonly refuses to touch an existing class, -class:updateonly refuses to create a new one, -class:safeupdate permits changes that do not conflict with child classes, and -class:forceupdate resolves conflicts by deleting the offending qualifier from the child — with both update modes failing outright if the child classes have instances. Two matching switches do the same for instances.
Three behaviours of the compiler deserve their own sentences, because between them they account for a large share of the frustration the subject generated.
The first is that a compile is not a transaction. In Microsoft's own wording, when an error occurs while updating the repository the compiler makes no attempt to return the repository to its state before processing began. A MOF file that failed halfway left the schema halfway changed, and putting it back was the author's problem. The compiler's return codes distinguish only four outcomes — success, failure to connect to the WMI server, an invalid switch, and a MOF syntax error — with anything else surfacing as a WMI error code from underneath.
The second is #pragma autorecover. If the repository is ever rebuilt, only the MOF files on a registered list are recompiled into the new one; a provider whose classes are not on that list simply ceases to exist after a rebuild, along with everything that depended on them. The compiler warns about this in as many words, telling the author that the file does not contain the pragma, that its contents will not be included if the repository is rebuilt in future, and that the statement belongs on the first line. The equivalent command-line switch adds the file to a list kept under the CIMOM key in the registry, and the documentation notes the obvious constraint that the listed files must be local, since a rebuild cannot reach across the network to fetch them.
The third is localisation, which MOF handles by splitting rather than by substitution. Compiling with the amendment switch produces two files: a language-neutral .mof with the amended qualifiers stripped out, and a language-specific .mfl carrying the localised text, destined for a child namespace named after the locale in the form MS_ followed by the hexadecimal Windows LCID — MS_409 for American English. A separate switch pair compiles a MOF file to a platform-independent binary form and validates it for WMI use, and another extracts binary MOF back out of a named resource. Those last are the driver author's switches, and they reappear in the kernel section below.
For those who preferred not to type MOF at all, Microsoft's WMI Administrative Tools included CIM Studio, a browser for the repository which could generate a MOF file from selected classes and compile one back in. It was the nearest thing the platform offered to a schema editor.
Writing a provider: the models and the registration
The thing the group's name really points at is provider development, and Microsoft's definition of a provider is worth quoting for its brevity: a COM object that acts as an intermediary between WMI and a managed object. In practice a provider is two artefacts — a MOF file that defines the classes it answers for, and a DLL that answers — and the shipped providers are the pattern. The Win32 provider, source of the Win32_ classes everyone queries, is CIMWin32.mof and CIMWin32.dll, both sitting in the WBEM directory.
Providers are classified by what they supply, and WMI recognises seven kinds:
- Instance providers supply the instances of a class. This is the common case, and all instance providers are pull providers: WMI asks them for data at the moment a client asks WMI, rather than holding anything in advance.
- Method providers implement the methods declared on one or more classes.
- Property providers supply or update individual properties within an instance.
- Class providers supply class definitions themselves, for schemas that are not known until run time.
- Event providers generate notifications.
- Event consumer providers receive them on behalf of logical consumers.
- Association providers supply the relationships between instances.
The documentation's own observation is that the vast majority of real providers are instance providers and method providers, and that the sensible practice is to combine every capability a component needs into a single provider registered several times over rather than shipping one provider per function. The shipped set illustrates it: the System Registry provider supplies instances and methods; the Disk Quota provider supplies instances, methods and events.
Registration is where the pieces are bolted together, and it is done in MOF. The author declares one instance of the system class __Win32Provider, giving the provider a Name, the CLSID of its COM object and a hosting model, usually with a MOF alias so the long object path need not be repeated. Then, for each capability, a further instance: __InstanceProviderRegistration, whose boolean properties declare whether the provider supports get, put, delete and enumeration; __MethodProviderRegistration; __EventProviderRegistration; __ClassProviderRegistration; __EventConsumerProviderRegistration. The same __Win32Provider instance is referenced by every one of them. Registration is not optional decoration — WMI can only access registered providers, the registration is what tells WMI which classes the provider answers for, and only administrators may register or delete one.
Nor is WMI the only registry involved. The provider is a COM server, so its CLSID must also be registered with COM in the ordinary way, which means the author is maintaining three things that must agree: the CLSID in the system's COM registration, the same CLSID in the __Win32Provider instance, and the provider's Name as quoted in the provider() qualifier of every class it serves. Any two of the three agreeing is not enough.
The code side begins with initialisation, which is common to every provider type. WMI loads a provider by calling IWbemProviderInit::Initialize; the provider does whatever setup it needs and then must report back through IWbemProviderInitSink::SetStatus, with WBEM_S_INITIALIZED to announce that it is ready or WBEM_E_FAILED to announce that it is not, in which case WMI regards it as non-functional. On success WMI queries the provider for its primary interface. For an instance provider that primary interface is IWbemServices — the client interface again, now facing the other way — and the provider implements the methods it supports, follows the documented semantics and error codes for each, and supplies stub implementations returning WBEM_E_PROVIDER_NOT_CAPABLE for everything else.
A provider did not have to implement query processing. WMI is able to filter the results of a provider that supplies no filtering of its own, by enumerating and discarding, which meant a minimal instance provider was genuinely minimal and a provider that did implement query support was making a performance decision rather than a correctness one. At the other end of the scale sat the high-performance interfaces — IWbemHiPerfProvider and the refresher machinery — for providers whose data was read repeatedly and quickly, including the data that appeared in the system's performance monitor.
Microsoft's own testing advice for provider authors is a fair portrait of how much could go wrong: exercise every interface of the provider, including the ones that do nothing, so that they at least return a proper not-supported error; call it from scripts, from managed code and from C++, because the three consumers exercise different paths; run it from a non-administrator context to confirm that impersonation works; and watch the provider operation events that WMI raises to find out whether the thing loaded at all.
Hosting, threading and the obligations of a long-lived object
A provider is not a program. It is a COM object loaded into somebody else's process, on demand, for as long as that process cares to keep it, and then unloaded. Most of what made provider development difficult follows from that sentence.
Where the object is loaded changed with the platform. On Windows 2000 and earlier a provider was loaded into the WMI service process itself, which is precisely as robust as it sounds: a faulty provider took the management service down with it, and a breakpoint in a debugger froze it. Later releases moved providers out into a separate host process named Wmiprvse.exe, of which more than one can run at a time, each under a different account and with different security. The provider's registration says which: the HostingModel property of the __Win32Provider instance takes values such as LocalSystemHost, NetworkServiceHost, LocalServiceHost, SelfHost and the paired forms that permit either, and a value of the form NetworkServiceHost:SomeName asks for a named host process shared only with providers that name the same group.
The defaults moved as the platform's security posture tightened. Beginning with Windows Vista, a provider with no hosting model specified no longer runs as LocalSystem; the default became the Network Service form, and a provider that explicitly asks for LocalSystem causes WMI to write events 5603 and 5604 to the event log so that administrators can see who is running privileged. The guidance attached to this is unambiguous and is a fair summary of the whole security design: most providers do not need LocalSystem, most providers must impersonate the client and perform their work in the client's security context, and a provider written on the assumption that it runs as the system will not behave properly once the default has moved out from under it.
A second hosting model arrived for components that are not always running. A decoupled provider lives inside the application it instruments rather than inside a WMI host, registers itself through IWbemDecoupledRegistrar, and declares a hosting model of Decoupled:Com. The trade is stated plainly in the documentation: decoupling puts the application, not WMI, in control of the provider's lifetime. The limitation is equally plain — decoupled providers can be instance, method, event and event consumer providers, but not class or property providers.
Threading follows from hosting. In-process providers run in a shared host and most types use the multithreaded apartment; the single-threaded apartment is supported only for instance, method, event and event consumer providers, which is to say that class and property providers must be prepared to be called on any thread at any time. Instance providers are, in the documentation's phrasing, strongly encouraged to use the Both threading model. None of this is unusual for COM; what is unusual is that the author does not control the process, the thread or the moment of the call.
Re-entrancy carried a rule with teeth. Every IWbemServices method that a provider implements receives a context pointer, and the provider must pass that same pointer into any call it makes back into WMI while servicing the request. The documented consequence of forgetting is not an error code but an infinite loop, since WMI has no other way to recognise that it is being asked to do something on behalf of a request it is already servicing. Two related prohibitions apply. Instance, class and property providers must not ask WMI to modify data while servicing a read request; the documented exception is the push provider, which keeps its data in the repository and may update it mid-read provided it flags the call as an owner update. Event providers must not change classes or touch event filters while servicing a call at all.
Unloading is the other half of the lifetime. WMI unloads providers to conserve resources, using an idle interval taken from the ClearAfter property of the cache-control instances in the root namespace, with the actual unload arriving somewhere between that interval and twice it. Whether a provider can be unloaded at all depends on a property of its registration: a pure provider exists only to service requests and can be released cleanly, while a provider that also acts as a client of WMI cannot be unloaded and costs the system overhead for as long as it lives.
Shutdown is where the obligations become genuinely awkward. If WMI is stopped, it may unload nothing: no call to the DLL's unload entry point, no destructors, possibly a thread terminated in the middle of a method, and at most a call to DllMain. A read-only provider does not care. A provider that writes is expected to implement something resembling a transaction model so that an abrupt termination can be rolled back, and to release by hand whatever the operating system will not release for it — the documentation's examples are sockets and database connections. Its suggested escapes are as revealing as the problem: put the cleanup in DllMain, put it in the destructor of a global object, or sidestep the whole question by running out of process and accepting the performance cost.
Even debugging inherited the architecture. A breakpoint stops the whole host process, which on a shared host means stopping every other provider loaded into it and blocking every client that calls them. The documented workaround is to give the provider its own host during development by setting the hosting model to a named group, and to set it back to the intended value before shipping.
Why writing one was regarded as difficult
Collect the requirements from the preceding sections and the reputation explains itself. A working provider is a COM server that is correct in three registrations at once, whose schema is installed by a compiler that does not roll back, which runs in a process it does not own, on a thread it did not create, under an account chosen by its registration, with an obligation to impersonate somebody else before touching anything, and with a lifetime that may end without warning and without cleanup. It must return the documented error for every operation it does not implement, must not call back into the service incorrectly on pain of a hang, and must be tested from three different kinds of consumer because each reaches it by a different path.
None of those requirements is unreasonable in isolation. What made the work hard is that a mistake in any of them surfaced identically: as a failure in somebody else's process, reported to the user as a hexadecimal code, at a moment determined by when a client happened to ask a question. The distance between cause and symptom is the reason a room full of people who had already made the mistakes was worth reading.
Microsoft's response was code generation. The WMI ATL wizard in Visual C++ produced a provider template from a class design, with a choice of in-process DLL or out-of-process executable, leaving the author to fill in the logic that fetched the actual data. The generated skeleton handled initialisation, registration boilerplate and the interface plumbing; it could not decide the hosting model, the threading model or the impersonation strategy, and those were exactly the decisions that later went wrong. A generator that produces a correct beginning but cannot produce a correct end leaves the hardest decisions exactly where it found them.
The managed alternative arrived with the .NET Framework and its own instrumentation namespace, and its successor arrived later still with Windows Management Infrastructure and its registration procedure for MI providers; both belong to the story told on the .NET WMI page rather than this one. What matters here is that for most of this group's working life, writing a provider meant writing C++, and the documentation said so.
The standard underneath
WMI is a Windows implementation of somebody else's design, and being precise about which parts came from where is worth the paragraphs, because it is the part most often blurred.
The standards body is the Distributed Management Task Force. It was founded in 1992 as the Desktop Management Task Force, and its first standard was the Desktop Management Interface; as its scope moved from the desktop to distributed systems, and as the Common Information Model became its principal work, it changed its name to the Distributed Management Task Force in 1999. It remains a nonprofit industry standards organisation whose members are the large hardware and infrastructure vendors.

The initiative is Web-Based Enterprise Management, sponsored in 1996 by BMC Software, Cisco Systems, Compaq Computer, Intel and Microsoft, and now published as a family of DMTF specifications. WBEM is not one thing but a stack: an information model, a set of protocols for reaching implementations of that model, discovery, and a query language.
The model is the Common Information Model. Its infrastructure specification defines the architecture and the metamodel — classes, properties, methods, associations, inheritance, all of it object-oriented and derived from UML — and the CIM Schema defines the actual common classes for computer systems, operating systems, networks, middleware, services and storage, extensible by vendors who need to describe something the common schema does not cover. MOF is the language that schema is written in. The DMTF publishes it as a standard in its own right, the Managed Object Format Specification, DSP0221, version 3.0.0 dated 13 December 2012, alongside the CIM metamodel document DSP0004.
What Microsoft implemented is the model and the language. The CIM_ classes in a Windows repository are the standard ones; the Win32_ classes derive from them; the files that define them are MOF; and the compiler that loads them is a MOF compiler. A schema designed for WMI is, in form, portable to any other CIM implementation.
What Microsoft did not implement is the wire. The WBEM protocol family's original transport is CIM-XML: CIM operations carried over HTTP, with the objects encoded in XML by the specification's own representation and document type definition. WMI does not speak it. Remote WMI connections are made through DCOM, which is Microsoft's own distributed object protocol and is understood by nothing outside the Windows world. The result is a genuine asymmetry that this group's members lived with daily: the schema was an industry standard, the access protocol was not, and a WMI provider was therefore reachable by any Windows client and by no standards-conforming WBEM client.
Microsoft also extended the model in ways its own documentation identifies. Embedded objects, as noted above, are supported by WMI and explicitly not CIM-compliant. The WMI system classes, with their double-underscore names, are WMI's own machinery for registration, security and eventing rather than part of CIM. There is a documented set of standard qualifiers specific to WMI, the provider-binding qualifiers among them. And WQL is Microsoft's query language, predating the DMTF's own CIM Query Language, which the DMTF published as DSP0202 version 1.0.0 on 13 August 2007, years after WQL was in daily use — so a query written against WMI is not, and never was, a standard query.
The convergence came later and by a different route. WS-Management, a SOAP-based management protocol, began outside the DMTF in a vendor coalition that started with AMD, Dell, Intel, Microsoft and Sun Microsystems and grew to thirteen members before the DMTF took it over in 2005; the DMTF published version 1.2 of the specification, DSP0226, on 30 September 2014. Microsoft's implementation of it is Windows Remote Management, and its arrival is the point at which Windows management data became reachable by something other than DCOM. The consequences for managed code and for the later CIM cmdlets belong to the .NET WMI page; the consequence here is narrower and structural. For most of this group's life, the answer to can I get at this from a non-Windows machine was no, and the reason was not the model but the transport.
The kernel side
The other half of the group's constituency did not call WMI at all. It answered.
WMI reaches into kernel mode as a set of extensions to the Windows Driver Model. WDM — announced at WinHEC in 1996 under the name Win32 Driver Model, and introduced with Windows 98 and Windows 2000 to replace the older VxD and NT driver models — arranges drivers in layered stacks that communicate by passing I/O request packets up and down. WMI's kernel route is built directly on that machinery: for a driver, providing management data is not a separate API but a major function code and a set of minor ones, handled in the same dispatch table as everything else the driver does. The coincidence of names is not a coincidence at all — the group sat in a branch called win32.programmer, and the driver model it reached down into had briefly carried the same adjective.
The driver author's first task is publishing a schema, and it is the same MOF language, compiled by the same compiler, into a different form. A driver's MOF file defines a class for each data block the driver exposes and each event block it can raise; the compiler is invoked with the WMI validation switch and the binary-output switch to produce a platform-independent binary MOF file, and the validation switch deletes its own output if any class in the file is invalid for WMI use. There are then three ways to publish the result: embed the binary MOF as a resource in the driver image, using a resource-script line that names it; put it in some other file and point at that file with a registry value under the driver's service key, so the schema can be updated without rebuilding the driver; or hold the binary data inside the driver and hand it over when WMI asks, which allows the schema to change at run time.
The Windows Driver Kit shipped a second tool for this path, Wmimofck.exe, which took the binary MOF as input, checked that its classes, properties, methods and events were valid for WMI, and then generated the connective tissue: a C header defining the GUIDs, structures and method indices from the MOF; a C source file of stubs for the driver's WMI code; a hexadecimal form of the binary MOF for drivers supplying it dynamically; and — the detail that says most about who this technology was for — a test application in VBScript or a set of rudimentary HTML pages for poking at the driver's data blocks by hand. It also merged the language-neutral and language-specific files back into a localised whole.

Registration is a two-phase handshake. The driver calls IoWMIRegistrationControl with the register action and a pointer to its device object, typically while starting the device and once it is able to handle WMI requests at all; WMI responds by sending a registration IRP, in reply to which the driver supplies its registry path, the name of its MOF resource, flags applying to all its blocks, and the information WMI needs to name instances — either a pointer to the physical device object or a base string from which static instance names are built.
Thereafter the driver is answering IRPs. Every WMI request arrives with the major function code for system control and one of a fixed set of minor codes: query all data or a single instance; change a single item or a single instance; enable or disable collection, for blocks the driver registered as expensive to collect; enable or disable events; execute a method; and the registration codes already mentioned. A driver may handle these itself in its dispatch routine, or it may call the kernel WMI library routine WmiSystemControl and let the library dispatch to a set of callback routines, described to it by a context structure holding a count of blocks, a list of per-block GUID registration entries and the callback entry points. The library route is available when instance names are static and derived from a base string or a physical device object; anything more dynamic and the author is back to handling the packets by hand.
One obligation applies to drivers that want nothing to do with any of this. All drivers must provide a dispatch entry point for system control requests; a driver that has registered must handle every WMI request, and a driver that has not must forward them to the next driver down the stack. Failing to forward breaks WMI for everything below you in the stack, which is a fine way to make somebody else's device disappear from a management console.
The two worlds meet in a user-mode provider. Driver-supplied data reaches clients through the WDM provider, itself a class, instance, method and event provider, whose own classes are defined chiefly in a file called Wmi.mof; the classes it creates to represent driver data exist only in the root\WMI namespace, which must already exist before it will process installed drivers, and it records what it does in a log file of its own, WmiProv.log. Driver events surface as a defined extrinsic event that consumers subscribe to like any other. A script reading a driver's data block is therefore talking to a provider that is talking to an IRP, and the boundary between the two is invisible from the query.
One vocabulary difference is worth flagging, because it caught people. In user mode a provider's classes are identified by name; in kernel mode a driver's blocks are identified by GUID, listed in the registration structure and mapped to class names by the schema. A driver author and an application author discussing the same data block could be some way into a conversation before discovering they were naming it differently.
Why the room mixed two trades
Two trades with very little else in common met in this group, and the reason is structural rather than social. It can be stated exactly.
WMI's repository does not record how a class is implemented. A Win32_ class backed by an in-process C++ provider, a vendor class backed by a decoupled provider inside a service, and a driver data block surfaced through the WDM provider are all, from the client's side, classes in a namespace with properties and methods. The same query language reaches all three; the same object path notation addresses all three; the same error codes come back from all three. That uniformity is the entire point of the design, and its side effect is that the population asking questions about a class has no reason to be homogeneous.
Diagnosis is where the two trades were forced into contact. A question that starts as why does this class return nothing can end in a mistyped provider name in a MOF qualifier, a hosting model that denied the provider the privilege it needed, an impersonation level that was never set on the proxy, a namespace that was never created, or a driver in the middle of a stack that swallowed an IRP it should have passed on. Those five answers come from several different specialities. In a room organised around the object model rather than around a profession, they arrived in the same thread.
The tooling made the same point without meaning to. A utility in the driver kit whose job was to validate a kernel driver's binary schema also generated a VBScript to test it, on the assumption that the driver author would want to try the thing from the consumer's side and would reach for the consumer's language to do it. That is the two trades in one command line.
The traffic from the administrators' side of the boundary — what they were doing with it, in which language, and what went wrong — is the subject of the Windows XP room's page, and the scripting language most of them used has its own room at microsoft.public.scripting.vbscript. What is worth recording here is only that the boundary was porous by design, and that a question of this kind announces which room it belongs in only after somebody has diagnosed it.
The group's own record
What survives of the group itself is administrative, and it is worth setting out precisely, because it is all there is.
The group is still in the namespace. The ISC's mirrored active file — the list from which a news server learns which groups exist and whether they accept posts — contains 1,770 entries under microsoft.public.*, and microsoft.public.win32.programmer.wmi is one of them. It carries the flag that marks a group unmoderated and open for posting, and an article range whose high-water mark sits below its low-water mark: a namespace entry rather than a spool with anything in it. The descriptive newsgroups file distributed alongside it carries no microsoft.* lines whatever, so the hierarchy has no surviving one-line self-description in the standard record, let alone a charter. It never had charters in the Big-8 sense either, for reasons set out on the hierarchy's own page.
A detail of the control record is worth noting because it is easy to assume otherwise. Control messages for the microsoft.* hierarchy are not issued by Microsoft. The widely distributed control.ctl file used by news servers records the hierarchy as maintained by an outside volunteer — described in the file as a Usenet participant acting to improve the propagation of the Microsoft groups — with a published PGP key, with newgroup and rmgroup messages from any other sender dropped, with microsoft.public.news.server named as the administrative group, and with msnews.microsoft.com listed as the syncable server. That last field has outlived the machine it names by well over a decade.
The end of the venue is not this page's story to tell: Microsoft announced that it would discontinue support for its public newsgroups from 1 June 2010, offering web forums in their place, and closed the msnews.microsoft.com server that year. The phased shutdown, with the company's own reasoning and the surviving documents, is described on the microsoft.public.* page. The group's own last day falls somewhere inside that window and is recoverable, if at all, only from the tail of its archive.
What the record does not settle
Several things a reader might reasonably want are simply not available, and inventing them would be worse than saying so.
- When it was created. No founding date for the group survives in any first-party source traced for this page. A vendor hierarchy created groups by decision rather than by vote, so there is no proposal, no discussion period and no result posting of the kind a Big-8 group leaves behind.
- How busy it was. No posting counts, subscriber estimates or traffic curves are published for individual microsoft.public.* groups, and none are asserted here.
- Who answered. There was no moderator, no roster and no register of recognised contributors by group. The people who did the answering are identifiable only from their own postings, and this page names none of them.
- How it divided with its neighbours. Four WMI rooms existed in the same namespace, including microsoft.public.wmi.programmer, and nothing in the surviving record establishes how questions actually distributed between them or whether the fourth ever displaced this one.
- Whether driver authors used it. The active file lists three device-driver groups of their own beneath microsoft.public.development.device.drivers. Whether kernel-side WMI questions were asked here, there, or in both, is a question about traffic, and traffic is what has not survived.
One methodological caution applies to everything technical above. The interface names, switches, qualifiers, hosting models and driver routines described on this page are verified against Microsoft's current published reference for WMI and for the driver model. Where that reference dates a behaviour — that a default hosting model changed with Windows Vista, for instance — the date is the documentation's. It is not a claim about what any particular poster was told at the time, and the reference has been revised more than once since the group was busy. The same caution applies in reverse to the group's own record: what is asserted about it here comes from the ISC active file and from the control.ctl distributed with news software, and nothing has been inferred from either beyond what those files literally contain.
Scope and limits of this page
This directory holds three pages about Windows Management Instrumentation because Microsoft's namespace held three rooms about it, and the division of labour between the pages follows the division between the rooms. What WMI is, how the repository and its providers fit together, and what the technology looked like from managed code are covered on the page for the .NET group. What administrators did with it, in which language, and which recurring failures dominated their working lives are covered on the page for the Windows XP group. This page covers the native-code half: the COM interfaces, the MOF language and its compiler, provider development, the standards underneath, and the route into device drivers.
For a reader arriving from an old citation, the useful thing to know is what the archived group is likely to contain. Not tutorials: the reference material existed and was competent. What accumulates in a group like this is the residue that documentation omits. The order in which two security calls must be made and what happens if they are not. Which switch to give the compiler when a schema change collides with a derived class that already has instances. Why a provider that works when the developer runs it fails when a service account calls it. What an unfamiliar hexadecimal code means. Which of four plausible layers is actually refusing.
That residue has aged unusually well, because its subject did. The interfaces described here are still documented as current, the compiler still ships in the same directory, the qualifier that binds a class to its provider is spelled the same way, and a driver still answers system-control packets. An answer written in this group in 2003 about IWbemLocator and a proxy blanket is, in the main, still executable. That is an accident of Microsoft's compatibility policy rather than any virtue of the newsgroup, but it is the reason the archive is worth the trouble of searching.
Reading microsoft.public.win32.programmer.wmi today
- Historical archive: Google Groups — microsoft.public.win32.programmer.wmi (coverage varies by group and era).
- Open in a newsreader:
news:microsoft.public.win32.programmer.wmi— the original site offered exactly this link, and it still works if your system has a newsreader registered for thenews:scheme. - Live access: point an NNTP newsreader at a modern server — see accessing Usenet today.
- The original news2mail e-mail subscription service ended in the mid-2000s and no longer operates.