news2mail.com

HomeMicrosoftPublic › Dotnet › Framework

microsoft.public.dotnet.framework.wmi

WMI programming from .NET.

Part of Microsoft’s public NNTP server (msnews.microsoft.com), where Microsoft ran hundreds of product support groups until retiring them to web forums in 2010. This one covered Windows Management Instrumentation from the .NET Framework: System.Management, WQL queries, providers and the marshalling surprises between them.

MVPs and Microsoft engineers answered directly; the threads document management-instrumentation practice of the 2000s Windows stack.

Long-form reference · 11,063 words · about a 48-minute read

Two vintages, one junction

The group sat at the junction of two Microsoft technologies of noticeably different age, and everything characteristic about it follows from that gap. Windows Management Instrumentation was late-1990s infrastructure: Microsoft's implementation of the industry's Web-Based Enterprise Management standards and the Common Information Model behind them, both published by the Distributed Management Task Force. It was offered as an add-on for Windows 95, 98 and NT 4.0 before shipping as a standard component from Windows 2000 onwards. The .NET Framework, by contrast, was released on 13 February 2002 alongside Visual Studio .NET, as the flagship of the largest platform bet Microsoft had made in years. This room existed for the people who had to make the new thing talk to the old one.

That framing decided the group's character before anyone posted in it. Questions here presumed C# or Visual Basic .NET, not the VBScript that carried most of the world's WMI traffic. A poster who asked how to enumerate processes was assumed to want a ManagementObjectSearcher, not a Win32 call and not a script file, and the answers came back as managed code with the COM exceptions of the underlying plumbing wrapped in try/catch. The group's working life follows the wrapper rather than the technology beneath it.

A Windows 2000 installation compact disc with French-language labelling, marked Service Pack 4.
A French-language Windows 2000 install disc carrying Service Pack 4, the last of the service packs, released in 2003. Windows 2000 was the first Windows release to ship WMI as a standard component; on Windows 95, 98 and NT 4.0 it arrived as a separate download. Don-vip · public domain · via Wikimedia Commons.

The asymmetry of ages is the intellectual content, not merely the chronology. WMI's design decisions were made for callers holding COM interface pointers and for scripting hosts that had no types worth speaking of; the runtime's were made for statically typed managed languages with a common type system and a garbage collector. Neither set of decisions was wrong. They simply did not agree about what an object is, what a property is, or when a value may be absent. A developer holding a ManagementObject was holding the seam between them, and the seam is what this page is about.

Within the sprawling dotnet.framework subtree this was one of the narrower rooms. The crowds gathered in the ASP.NET and Windows Forms groups; instrumentation drew a smaller mix of developers who had been handed a management requirement and administrators who had drifted into writing compiled code. The name did the filtering on its own: each dot in microsoft.public.dotnet.framework.wmi narrowed the audience, and by the fifth component the readership was people with a WQL query open in one window and a compiler error in the other.

The paperwork, and what a vendor hierarchy did instead

There was no Request for Discussion for this group, no Call for Votes, no vote tally and no RESULT posting, because none of those things applied. microsoft.public.* was a vendor hierarchy running on Microsoft's own public news server, and its rooms were created administratively by the company's support organisation whenever a product team wanted one. The elaborate creation ritual of the Big Eight — proposal, weeks of public argument, a neutral votetaker, a supermajority and a waiting period — has no counterpart anywhere in this branch's history. Anyone looking for founding documents for this group will not find them, and this page does not manufacture any.

What does survive is the standard record, and it is thin in an instructive way. A news server learns which groups exist, and whether each one accepts posts, from its active file; the Internet Systems Consortium mirrors Usenet's. That mirror still lists microsoft.public.dotnet.framework.wmi as unmoderated, and its article counters were never advanced. The companion newsgroups file, the descriptive list from which newsreaders build a browsable listing, does carry a line for the group, but the line is not a self-description. It reads “Microsoft .NET technology newsgroup.”, and that same sentence stands verbatim against all forty-nine names in the microsoft.public.dotnet subtree; the other microsoft.* families are labelled the same way, one boilerplate sentence per product. Nothing in the standard record says what this room was for beyond the word wmi at the end of its name. The one-line description above this article is an editorial summary written for this directory; it is not a charter, and the hierarchy never had charters in the Big-Eight sense.

The control-message archive is more forthcoming about the neighbourhood than about the room. On Sunday 12 November 2000, between 18:13:57 and 18:13:58 Pacific time, [email protected] issued newgroup messages for the parent group microsoft.public.dotnet.framework and for adonet, aspnet, aspnet.mobile, aspnet.webservices, clr, interop, sdk, sdk.setup and windowsforms beneath it: ten names in two seconds. On Monday 27 November the same address issued the same ten again, this time all bearing the identical timestamp of 10:39:50 Pacific — the signature of a script working through a list rather than of anyone deliberating.

Earlier still, on 28 August 2000, the same address created microsoft.public.dotnet.framework.classes.general, .runtime.general and .sdk.general at 09:29:02 Pacific and issued rmgroups for all three at 10:44:54 the same morning; two further names, .aspplus.general and .sdk.setup, went out at 10:32:09 and survived the day. The aspplus name is a fossil: ASP+ was the codename under which ASP.NET was first announced to the public. Its room was removed on 21 November 2000 — nine days after microsoft.public.dotnet.framework.aspnet had been created, so the rebranded room was already open when the codename's room was retired. The namespace kept pace with the rebranding, to the week.

For this group, nothing. There is no surviving newgroup or rmgroup control message anywhere in the archive for microsoft.public.dotnet.framework.wmi, even though the archive went on collecting microsoft.* control traffic for years afterwards — the newgroup for microsoft.public.dotnet.framework.microframework.porting is dated 20 October 2008. The honest conclusion is a negative one. The date this room was opened is not recoverable from the standard record, and this page will not guess at it.

One further document is worth quoting exactly, because it shows what a vendor hierarchy's paperwork was worth outside the vendor's own server. On 2 November 2001 a Usenet hierarchy administrator, Ed Hew of XeniTec Consulting Services in Kitchener, Ontario, signing as the biz newsgroup authority, issued an rmgroup for the parent group microsoft.public.dotnet.framework, cross-posted to biz.config, biz.control and news.groups. His stated reason was to

counter numerous forged newgroup commands, and remove bogus newsgroup: microsoft.public.dotnet.framework which was never created in accordance to the accepted rules and methods for this hierachy.

The message reported “over 100 forgeries so far” and promised to repeat itself at intervals until the problem was dealt with. Whatever one makes of the dispute — and it is documented here rather than adjudicated — it states the situation precisely. On msnews.microsoft.com this was simply a room the support organisation had opened. Out on the wider network, where every administrator decides individually which control messages to honour, its paperwork counted for nothing at all.

Where the room sat in the branch

The active file establishes the shape of the neighbourhood even where it says nothing about the rooms themselves. There are 1,770 microsoft.* names in it. Forty-nine of them sit under microsoft.public.dotnet, and the framework subtree is much the largest of those: microsoft.public.dotnet.framework has twenty-nine children. Its siblings are adonet; aspnet and eight rooms beneath it, for announcements, control building, caching, the datagrid control, mobile, security, web controls and web services; clr; compactframework; component_services; drawing; interop; microframework and microframework.porting; odbcnet; performance; remoting; sdk; setup; webservices and webservices.enhancements; windowsforms and three rooms beneath it, for controls, data binding and design time; and wmi.

Read that list as a filing system and one entry stands out. The siblings are named after parts of the framework itself (clr, drawing, remoting), after application models built on it (aspnet, windowsforms), after how it was delivered and installed (sdk, setup, performance), or after the boundary with unmanaged code (interop, component_services). One is named after something the framework did not contain at all: a Windows service that predated the runtime by several years and was not part of .NET in any sense. WMI is not a .NET technology. System.Management is. The room is filed under the wrapper.

It is worth saying plainly what that implies, because it is easy to over-read. The branch structure of this hierarchy was a support queue's, not a taxonomist's. A framework subtree acquired a WMI room because enough people were writing to Microsoft about System.Management specifically to justify one, not because anybody at Microsoft believed WMI belonged to the framework. The room is evidence of correspondence volume about a wrapper, which is a genuinely different claim from evidence of architectural intent.

The near-neighbour that makes the point clearest is not another WMI room at all but microsoft.public.win32.programmer.directx.managed, which served Managed DirectX. The two rooms are mirror images of the same filing decision. There, a managed wrapper got a room inside the native technology's branch, named after the runtime. Here, a native technology got a room inside the managed branch, named after itself. Both rooms existed for the same reason: a wrapper generates its own questions, distinct from the questions the wrapped thing generates, and somebody has to answer them.

Five components is a long name even by this hierarchy's standards, and this hierarchy's standards were the deepest in the file. Of the 1,770 microsoft.* names, 489 are five components long and the mean depth is 4.48 components, against 3.56 for the active file as a whole; no other hierarchy of more than three hundred groups in the same file averages more than 4.07. Depth here was a proxy for narrowness, and narrowness was the point: a room this specific cost Microsoft nothing to open and gave the product team a place to watch one API meet the public.

WMI itself received four rooms in this hierarchy, split by audience rather than by subject: microsoft.public.wmi.programmer directly beneath the stem, microsoft.public.win32.programmer.wmi for the native API, microsoft.public.windowsxp.wmi for administrators working from the operating-system end, and this one for callers holding managed objects. The boundaries were porous in the usual Usenet manner, and cross-posting among the set was routine.

The venue, and why a room this narrow was viable

The technology the branch was named after began life in the late 1990s under the name Next Generation Windows Services, went public under the .NET brand in 2000 — which is exactly when the first framework rooms were created — and moved through visible betas during 2001 before the February 2002 release. For its first years the framework was not part of Windows. Version 1.0 arrived as its own installation; its Service Pack 2, released on 7 August 2002, was the first build to travel on operating-system media, as an optional component of Windows XP Service Pack 1; and version 1.1, released in April 2003, was the first version built into a Windows release, shipping with Windows Server 2003. For several years, in other words, the framework was something developers downloaded, installed, argued about and needed help with.

Help had an official address, and the economics of that address are the reason a five-component room could stay viable on modest traffic. An NNTP group cost nothing to read and nothing to ask. Anyone with a newsreader — and Outlook Express was already sitting on the Windows desktop, which made it the path of least resistance — could pull the group down, search what had already been answered, and post into a conversation that Microsoft's own engineers were known to read. For a developer facing an access-denied at eleven at night, that was a materially different proposition from opening a paid support incident.

The medium also suited the work. A threaded newsgroup keeps a problem, its diagnosis and its resolution attached to one another, in order, under one subject line, which is precisely the shape of a debugging session. That is why the archives of rooms like this one read as reference documents rather than as chatter, and it is a large part of why they were still being cited a decade after the last post.

The end of the server, and where the questions were sent

The hierarchy ended all at once. In the spring of 2010 Microsoft announced that it would close its public newsgroups and move their users to web forums — Microsoft Answers for consumers, TechNet and MSDN for professionals and developers — and the closure proceeded in phases from June that year. The full story of that decision, including the company's own accounting of more than 2,000 public newsgroups and its stated reasons for retiring them, is told on this directory's page for microsoft.public.word.numbering and is not retold here.

What belongs here is the mapping, because Microsoft published it and it survives. The company's newsgroups page, as archived in the summer of 2010, carries a heading reading “Newsgroups Deleted June 1, 2010” followed by 1,434 group names, almost every one of them hyperlinked to the forum intended to replace it, and then a second heading, “Newsgroups Deleted June 1, 2010 with no direct forum mapping”, followed by 92 more. microsoft.public.dotnet.framework.wmi is in the first list, so it had a mapping.

Its mapping is not to a forum. Where microsoft.public.dotnet.framework.clr and .interop both point at the MSDN forum for the common language runtime, and the Windows Forms rooms point at the winforms forum, with the data-binding room sent to winformsdatacontrols, this group's link goes to the MSDN category index for .NET Development — the top of a tree rather than a room within it. Its three WMI cousins fared much the same: the native room was pointed at the Windows SDK forum, and the Windows XP and wmi.programmer rooms at category pages. Not one of the four WMI rooms in the hierarchy had a successor forum devoted to WMI.

That is as close as the surviving record comes to a judgement on the room's specialisation, and it is a quiet one. Five components deep was finer-grained than the replacement medium was prepared to be. A reader arriving at the old address in 2011 with a System.Management question was handed a category listing and left to find their own way.

What WMI was, in the form a managed developer needed

Only a compressed account of the technology belongs on this page, because two neighbouring pages carry it at length. The full native story — the COM interfaces a C++ client called, the Managed Object Format files from which the repository is loaded, the compiler that loads them, what writing a provider involved and what the DMTF standard underneath actually specifies — belongs to microsoft.public.win32.programmer.wmi. The administrator's story — the scripting idiom, the command-line query tools, repository repair, firewall and DCOM failures, inventory practice and the eventual succession to PowerShell — belongs to microsoft.public.windowsxp.wmi. What follows is only the part a managed caller needed in order to understand what their wrapper was wrapping.

WMI has three moving parts. Providers are components that each answer for some domain of the machine — one knows about processes, another about the event log, another about disks. The WMI service, WinMgmt, brokers between those providers and any client asking questions; in WBEM vocabulary it is the CIM Object Manager. And the CIM repository holds the class definitions that describe what can be asked. The classes are objects in the strict sense: properties, methods, inheritance, and associations for the relationships that inheritance cannot express.

Block diagram of the Windows 2000 operating system architecture, labelled from hardware and the hardware abstraction layer up through kernel-mode executive services to user-mode integral and environment subsystems.
The layered architecture of Windows 2000, the release that made WMI a standard component, in a block diagram drawn for Wikipedia in 2005. It maps hardware, the kernel-mode executive and the user-mode subsystems; the WMI service and its providers ran among the user-mode components, and are not themselves drawn here. The original uploader was Grm wnr at English Wikipedia. Later versions were uploaded by Xyzzy n at en.wikipedia. · CC BY-SA 3.0 · via Wikimedia Commons.

The repository is partitioned into namespaces, and knowing which one you are standing in is half the discipline. The classes describing the operating system — the Win32_ family and the CIM_ classes they derive from — live in root\cimv2. That namespace is also the default scope for System.Management, which is why so much managed WMI code never names a namespace at all, and why a query that compiles, runs and returns nothing has so often simply been aimed at the wrong one. Suggesting a check of the connection's scope was among this room's most reliable first responses.

Clients ask in WQL. Its surface is reassuringly SQL-shaped, and the resemblance is exactly deep enough to mislead: there are no joins. Relationships are walked with ASSOCIATORS OF and REFERENCES OF rather than joined, and both have direct counterparts in the managed query types. Queries also run in the other temporal direction, subscribing to change rather than reading state, and many of the intrinsic events are synthesised by polling — the interval named by the query's WITHIN clause. That polling is the reason a managed event watcher can appear to fire seconds late or to deliver two changes as one, and it surprised posters in this room for a decade.

Two further facts about WMI itself belong here, because between them they explain why the subject would not go away. WMI is built as a set of extensions to the Windows Driver Model, so kernel-mode components surface information through the same door as everything else; and it grew. The first implementation appeared in the Windows NT 4.0 Service Pack 4 era as an out-of-band download, and that NT 4.0 package shipped with fifteen providers; Windows 2000 with twenty-nine; Windows Server 2003 with roughly eighty; Windows Vista with about a hundred. Hardware vendors wrote providers for their own equipment — Intel for its network adapters, for instance — while management suites including HP OpenView, IBM's Tivoli portfolio, Microsoft Operations Manager and Systems Management Server consumed WMI as a data source. From Windows XP Service Pack 2 the operating system itself leaned on it, reading antivirus and firewall status through WMI. By the time this newsgroup was busy, WMI was not an optional extra but the plumbing under a good part of Windows management.

One tool matters to this page specifically. Windows ships wbemtest.exe, a raw dialog-box harness exposing most of the API surface, present on every NT-based installation and, in the fine tradition of indispensable Microsoft tooling, not officially supported. It sits below the managed layer, which made it the standard instrument of elimination in this room: reproduce the failing query in wbemtest, and if it fails there too the fault lies with the service, the provider or the target machine's security and the thread belongs next door; if it succeeds, the wrapper is the suspect and the group is in its element. The rest of the toolbox — WMIC, WBEMDump, the WMIDiag diagnostic script — is the administrators' page's subject.

System.Management: the catalogue

The class library of .NET Framework 1.0 included System.Management from the beginning: a managed wrapper over the COM-based WMI client interfaces, giving C# and Visual Basic .NET programs a route into the repository without a line of interop declaration of their own. It is worth walking through what the namespace actually contained, because the shape of the catalogue explains most of what the room spent ten years discussing.

Scope and path

ManagementScope represents a namespace on a local or remote machine. It carries a ManagementPath and a ConnectionOptions, exposes IsConnected and an explicit Connect(), and is the object that a caller reuses if they have understood that a connection costs something. ManagementPath parses and builds the object paths by which WMI addresses things, exposing Server, NamespacePath, ClassName and RelativePath as separate properties, a static DefaultPath, and the trio IsClass, IsInstance and IsSingleton.

ConnectionOptions is where DCOM shows through the managed surface. It carries Username, Password (later joined by SecurePassword), Authority, Locale and EnablePrivileges, plus two enumerations lifted directly from COM security: ImpersonationLevel, whose members are Default, Anonymous, Identify, Impersonate and Delegate, and AuthenticationLevel, whose members are Unchanged, Default, None, Connect, Call, Packet, PacketIntegrity and PacketPrivacy. The reference names Impersonate as the recommended level for WMI calls and warns that calls may simply fail at Anonymous or Identify. A great many developers met COM's security model for the first time as these two enumerations, without ever learning that was what they were meeting.

Objects and collections

ManagementBaseObject is the root of the object model and the source of half this page's later argument. It exposes an indexer taking a string and returning object; a Properties collection of PropertyData; a separate SystemProperties collection for the properties whose names begin with two underscores; a Qualifiers collection of QualifierData for CIM's metadata-about-metadata; GetPropertyValue and SetPropertyValue as method-shaped alternatives to the indexer; CompareTo with a ComparisonSettings flag set; and GetText, which will render the object as MOF or as CIM or WMI XML according to a TextFormat enumeration.

ManagementObject derives from it and represents a single instance, adding Get(), Put() with a PutType of UpdateOnly, CreateOnly or UpdateOrCreate, Delete(), CopyTo, InvokeMethod, GetMethodParameters, and GetRelated and GetRelationships for association traversal. ManagementClass represents a class definition rather than an instance, adding GetInstances, GetSubclasses, CreateInstance and Derive. Results arrive as a ManagementObjectCollection with a nested ManagementObjectEnumerator. Method invocation is a two-object dance: GetMethodParameters hands back a ManagementBaseObject to fill in, and InvokeMethod hands back another to read out.

Queries and searchers

ManagementQuery is the abstract base. Beneath it, ObjectQuery and WqlObjectQuery and SelectQuery cover data queries; RelatedObjectQuery represents a WQL ASSOCIATORS OF query and RelationshipQuery a REFERENCES OF query; EventQuery and WqlEventQuery cover subscriptions. ManagementObjectSearcher takes a query, optionally a scope and optionally an EnumerationOptions, and returns a collection from Get(). It is, in Microsoft's own phrasing, one of the more commonly used entry points to retrieving management information, and it is the type this room's traffic revolved around.

Options and failures

Every operation has an options object, and all of them descend from ManagementOptions, which carries a Timeout and a Context of named values passed straight through to the provider. The derived set is EnumerationOptions, ObjectGetOptions, PutOptions, DeleteOptions, InvokeMethodOptions, EventWatcherOptions and ConnectionOptions. Failures arrive as ManagementException carrying a ManagementStatus — WMI's own error-code enumeration surfaced as a CLR enum rather than as a bare HRESULT, which was one of the wrapper's genuine kindnesses.

Two remaining types exist purely because the two type systems disagree, and both get sections of their own below: CimType, the enumeration of CIM property types, and ManagementDateTimeConverter, a static converter between the DMTF's date-time strings and the framework's DateTime and TimeSpan.

What an ordinary retrieval looked like

Structurally it was four moves and a loop. Name a scope, or accept root\cimv2 by default. Build a query. Hand both to a ManagementObjectSearcher. Call Get(). Iterate the collection that comes back, reading properties out of each object by name through the string indexer. MSDN Magazine's introduction to the namespace, published in May 2002 under Zina Pozen's byline, printed the canonical form in four lines: a searcher constructed directly from a WQL string, a foreach over searcher.Get(), and a line of output reading a caption out of each service by name.

Two of those four lines contain strings the compiler cannot check. The class name is a string. The property name is a string. The namespace, when given, is a string. The query is a string. All four are validated at run time against a repository whose contents differ between Windows versions, service packs and machine roles. The design is late-bound throughout, deliberately and for good reasons of its own, and it kept a particular genre of typo-hunting thread in steady supply for the group's entire life.

Nothing about that shape was hard, and the room's beginner traffic reflected it: the questions were rarely about how to write the four lines. They were about everything the four lines decline to state — which class holds the value you want, whether the property exists on the version of Windows you are pointing at, what account the code will run as, and why the same four lines behave differently on the server. The answers were correspondingly specific, and their specificity is why the archive is worth searching before posting.

Three ways of waiting

The namespace offered three temporal modes, and confusion between them accounted for a steady share of the traffic.

The first is straightforwardly synchronous: call Get(), walk the collection, block while it happens. The second is semisynchronous, and it is the one that catches people. EnumerationOptions.ReturnImmediately selects, in the documentation's own words, whether the operation is performed “in a synchronous or semisynchronous fashion”: set, the enumeration is invoked and the call returns immediately, and the actual retrieval of results happens as the resulting collection is walked. Alongside it sit Rewindable, which decides whether the collection holds its objects for repeated enumeration or may be walked only once, and BlockSize, which decides how many results the service returns per fetch. The practical consequence is that the timing of a WMI failure need not be the timing of the call that caused it: an exception can surface out of the foreach rather than out of Get(), which makes a stack trace point at the wrong line.

The third is genuinely asynchronous, and it is a period piece. ManagementOperationObserver — documented as managing asynchronous operations and handling management information and events received asynchronously — is passed to asynchronous overloads that exist all over the namespace: searcher.Get(observer), class.GetInstances(observer), object.Put(observer), object.InvokeMethod(observer, ...), class.GetSubclasses(observer). The caller attaches handlers to the observer's events before starting the operation and results arrive one at a time. The events are ObjectReady, delivering each result through ObjectReadyEventArgs.NewObject; Completed, with a CompletedEventArgs; Progress; and ObjectPut. Each has its own delegate type, and the observer carries a Cancel() for abandoning the operation.

That shape deserves naming precisely, because the framework later standardised something that resembles it and this namespace was never converted. It is not the Begin/End pair of the asynchronous programming model. It predates by three years the event-based asynchronous pattern that .NET 2.0 formalised. And when tasks and the await keyword arrived in .NET Framework 4.5 in 2012, nothing happened here: there is not one task-returning member anywhere in System.Management today, in the current package, more than two decades after 1.0. Code that wants awaitable WMI still wraps the observer by hand, exactly as it did in 2002.

Event subscription proper is ManagementEventWatcher, a component carrying a Query, a Scope and an EventWatcherOptions. It too offers two ways to wait. WaitForNextEvent() blocks until an event arrives or until the options' timeout expires. Alternatively Start() begins delivery on a callback thread, with a handler attached to the EventArrived event reading EventArrivedEventArgs.NewEvent, and a Stopped event and a Stop() to cancel the subscription.

Microsoft's own documented example for the class is a compact catalogue of this room's recurring difficulties. It builds a WqlEventQuery over the intrinsic event class __InstanceCreationEvent with a polling interval of one second expressed as a TimeSpan, and a condition of the form TargetInstance isa "Win32_Process". Then, to read the new process's name and path, it reaches into the delivered event's TargetInstance property, which is an embedded object rather than a value, and must be cast to ManagementBaseObject before it can be indexed — twice, on one line. The polling interval, the embedded object and the cast are three of the group's staple subjects, all present in the vendor's own published sample.

The failures around watchers were correspondingly structural rather than clever. A watcher created in a local variable and left to the collector stops firing at an unpredictable moment. A handler that throws does so on a thread with nowhere to report it. A subscription whose interval is finer than the provider can deliver produces coalesced events, or events arriving later than the interval implies. And a temporary subscription dies with the process that made it, which is what WMI's permanent event consumers exist for — and those are registered in the repository rather than written in C#, which put them squarely in the native room next door.

Publishing, not only reading: the first instrumentation model

The traffic ran overwhelmingly in one direction. Most shops wanted data out of WMI, not into it, and the query side dominated the room accordingly. But the framework shipped the other direction too, in a namespace of its own, and this is the part of the subject that belongs to this page and to no other: a managed application could surface its own state as WMI classes and instances, and fire its own WMI events, without anyone writing a provider in C++.

The first model lives in System.Management.Instrumentation, in System.Management.dll, and dates from 1.0. It is attribute-and-base-class shaped, and small enough to describe completely. Mark the assembly once with InstrumentedAttribute. Mark a class with InstrumentationClassAttribute, passing InstrumentationType.Instance to expose its instances as management data or InstrumentationType.Event to make it fireable as an event. Where you control the class hierarchy, derive from Instance or BaseEvent, which supply implementations of IInstance and IEvent; where you do not, implement those interfaces directly. Rename a class or member for the outside world with ManagedNameAttribute; hide a member from instrumentation entirely with IgnoreMemberAttribute.

Then call the helpers on the Instrumentation class, which the reference describes as having a single instance per application domain: Publish(object) makes an instance visible through management instrumentation, Revoke(object) withdraws it, Fire(object) raises an event, RegisterAssembly and IsAssemblyRegistered deal with the schema, and SetBatchSize caps how many objects of a given type are handed over at a time.

The inversion at the centre of this is worth dwelling on. In the ordinary WMI development order, the CIM class is authored first and code is written against it. Here the class definition is the code, and the CIM schema — along with the provider registration — is generated from it. That is the single most attractive thing the managed instrumentation surface offered, and it is the reason it existed: it let a developer instrument an application without learning the schema language at all.

The bill arrives at deployment. The generated schema has to be registered on the machine where the application will run, which is an install-time step: derive a class from DefaultManagementInstaller or DefaultManagementProjectInstaller, or include a ManagementInstaller in the project installer, and see that it is actually invoked — through an installer package, through installutil.exe, or programmatically. Miss that step and the instrumented application runs perfectly and is completely invisible, which is a distinctive and rather demoralising class of bug.

Architecturally the model is a decoupled provider: the application controls its own lifetime rather than being loaded on demand when a consumer asks, which is the natural fit for instrumenting something that is already running, and which meant — per the same 2002 MSDN Magazine account — that the overhead of calling the instrumentation APIs when nothing is listening is minimal. What decoupled providers are, how the subsystem hosts them and what obligations that places on a long-lived object are the native room's territory.

The second model: attributes all the way down

Later the framework acquired a second and much larger instrumentation surface in the same namespace, documented by Microsoft as the WMI.NET Provider Extensions. It is easy to tell the two apart in the reference even without dates: the first model's types are in System.Management.dll, and the extensions' attributes are in System.Core.dll — the assembly that also carries LINQ's Enumerable, and therefore a considerably later arrival than the namespace it joined.

Where the first model published objects, the second describes a provider declaratively, and the attribute list reads as a CIM class declaration written in C# syntax. WmiConfigurationAttribute goes on the assembly and establishes the high-level configuration of the provider being implemented: the WMI namespace it serves, plus a HostingModel drawn from the ManagementHostingModel enumeration, a HostingGroup, a Scope and a SecurityRestriction. ManagementEntityAttribute marks a public class as a WMI class, with Name, Singleton and External properties.

Within the entity, the member attributes cover the whole of a CIM class's surface. ManagementKeyAttribute identifies key properties. ManagementProbeAttribute marks a property or field as a read-only WMI property and ManagementConfigurationAttribute as a read-write one, with a ManagementConfigurationType choosing the commit behaviour and ManagementCommitAttribute marking the method that writes a set of read-write properties in one atomic operation. ManagementTaskAttribute marks a method that implements a WMI method. ManagementEnumeratorAttribute marks the method returning all instances of the class and ManagementBindAttribute the method returning the instance matching a given key; ManagementCreateAttribute and ManagementRemoveAttribute mark creation and clean-up. ManagementReferenceAttribute marks a member or parameter as a reference to another management object. ManagementQualifierAttribute attaches CIM qualifiers, with a ManagementQualifierFlavors enumeration for their inheritance behaviour, and ManagementNameAttribute overrides the names exposed. InstrumentationManager handles the lifetime and model of decoupled providers, and WmiProviderInstallationException reports registration failures.

The result is a complete WMI provider written as annotated C#, with the schema inferred from the annotations rather than authored separately. It is the most thorough answer the managed world ever gave to the question this group was founded on, and it is also, in the shape of the room's traffic, a minority interest that arrived late. The query side had a decade's head start and never lost it.

A boundary is worth restating here, because the temptation to cross it is strong. What the hosting models actually mean, how a provider is registered with the service, what threading obligations a long-lived provider assumes, and what the schema language looks like are all the native room's subject and are covered there. What belongs on this page is the narrower fact: through this attribute set a managed developer could reach all of it in one language, without ever opening a schema file. The schema was still there, and still governed what a consumer would see; it had simply been moved out of sight into the compiler's output.

The impedance mismatch

Here is the part of the subject that belongs to no other page in this directory: what happens when a loosely typed CIM object graph surfaces inside a strongly typed runtime. Every recurring difficulty in the room can be traced to it, and Microsoft was candid about it from the start.

Everything WMI returns is a bag of named values. ManagementBaseObject's indexer takes a string and returns object; properties are reached through a Properties collection whose members expose a Value of type object; a class's methods are catalogued in a Methods collection on ManagementClass and invoked on an instance by name. Reading a disk's free space in C# therefore meant casting a boxed value out of a bag, and MSDN Magazine printed exactly that line in May 2002 — immediately above the observation that code manipulating late-bound objects “is awkward to write and even more awkward to read, does not take advantage of the convenient IntelliSense technology in Visual Studio .NET, robs the developer of compiler-time type safety, and requires constant use of supporting documentation”. Directly beneath the complaint the author set the line every C# programmer wanted instead: a typed property read, four words long. That is the vendor's own assessment of its own API, three months after it shipped.

The collections never became generic, and the fact is easy to verify because it is still true. ManagementObjectCollection implements ICollection and IEnumerable — the non-generic ones — and so do PropertyDataCollection, MethodDataCollection and QualifierDataCollection, in the current .NET package as in 1.0. Generics arrived with .NET Framework 2.0 in 2005 and LINQ with 3.5 in 2007. A foreach over a searcher's results still yields object and still needs a cast; querying one with LINQ still begins with a Cast<ManagementObject>(). Two decades of language evolution went past the outside of this namespace without touching it.

The type map itself is small and mostly sane. CimType enumerates SInt8 through UInt64, Real32 and Real64, Boolean, String, Char16, DateTime, Object for an embedded object, Reference for a path to another object, and None for a null value. The documented mapping into the CLR is what one would expect: SInt32 to Int32, UInt64 to UInt64, Real64 to Double, Boolean to Boolean, Char16 to Char. Half of CIM's integer types are unsigned, which matters more than it sounds: a value that arrives boxed as a UInt32 and is cast to int throws at run time rather than converting, and the compiler cannot help because it never knew what was in the box. It is a run-time failure with no compile-time warning anywhere in front of it, which is precisely the kind of thing a newsgroup exists to explain.

Embedded objects and references are where the model departs furthest from anything the CLR has. An embedded object has no path and its lifetime is identical to that of the object containing it; it surfaces as another ManagementBaseObject that must be cast before it can be indexed. A reference is a path to a separate object, represented as a string. The published reference for CimType describes Reference as “a string containing the path to the referenced object” and then, in the very next sentence, states that the value “maps to the Int16 type” — a contradiction that has stood in the published reference for two decades.

Then there are qualifiers, which have no CLR analogue at all. Every class, instance, property and method in CIM may carry qualifiers: metadata about the metadata, with flavours governing whether they are inherited. The runtime has attributes, but attributes are compile-time artefacts of an assembly, whereas qualifiers are live repository data that can differ between machines. System.Management surfaces them as QualifierData collections hanging off the object, reachable only by name, and some of them are load-bearing: EnumerationOptions.UseAmendedQualifiers decides whether the localisable descriptions attached to classes and properties come back with the data at all.

Finally, the system properties. Every WMI object carries a set of properties whose names begin with two underscores — __PATH, __RELPATH, __SERVER, __CLASS — which are not part of the class's schema and live in their own SystemProperties collection. EnumerationOptions.EnsureLocatable exists purely to guarantee that a query returns them non-null, and the documentation notes that the flag applies to queries only and is ignored in enumerations. The consequence is a genuine trap: a SELECT with a property list is not merely an optimisation, because a projection that omits the system properties yields objects that cannot afterwards be located, refreshed or acted upon.

Dates, nulls and arrays

Three specific seams generated enough traffic on their own to deserve separate treatment.

The first is dates, and it is the seam with the most conspicuous documentation trail: the framework shipped a dedicated converter class for this and for nothing else in the namespace. CIM's date-time type is a string in a fixed layout, and the reference states the layout exactly: yyyymmddHHMMSS.mmmmmmsUUU — eight digits of date, six of time, a full stop, six digits of microseconds, then a sign and a three-digit offset from UTC. It arrives in managed code as a String. DateTime will not parse it, and neither will any of the framework's standard format providers, which is why an unprepared developer's first act on printing an installation date was often to post the result somewhere and ask what it was.

The namespace's answer is ManagementDateTimeConverter, a sealed class of four static methods: ToDateTime(String), which returns a DateTime in the current time zone of the system; ToDmtfDateTime(DateTime) to go back the other way; and ToTimeSpan(String) and ToDmtfTimeInterval(TimeSpan) for the interval form, which uses fields of the same widths to mean elapsed time rather than an instant. Two traps follow directly from that description. The returned value is in the machine's current time zone, so the same string read on two machines in two zones produces two different DateTime values — a fine source of reports that a fleet inventory disagrees with itself. And the interval form and the instant form are different formats with different converters, so a value that looks like a date and is in fact a duration will convert without complaint into nonsense.

The second seam is absence. A property may be missing from the class entirely, present in the class but unfilled by the provider, or filled with an empty array; and the type enumeration itself has a member, CimType.None, described simply as a null value. Reading a property the class does not define fails; reading one the provider did not fill returns null. From the returned object alone, a caller cannot distinguish “this machine has no such value” from “this provider does not implement that property on this version of Windows”. That indistinguishability is precisely why so many good answers in this room came qualified by operating-system version, and why the qualification was so often the part the documentation had omitted and the part the asker actually needed.

The third is arrays. Any CIM type may be an array, and the wrapper generator's own type table disposes of the whole matter in a single line: CIM_ARRAY maps to an array of the above-mentioned objects. In the bag, that means the indexer returns an object that is really a String[] or a UInt32[], and a caller who casts to the scalar type gets an invalid-cast exception at run time rather than a diagnostic at compile time. Arrays, embedded objects and unsigned integers are the three shapes in which the bag most reliably surprises a caller who has assumed a scalar.

The strongly typed wrapper, and why it did not settle the matter

Microsoft's answer to all of the above was code generation, and it shipped in the box. The SDK carried Mgmtclassgen.exe, the Management Strongly Typed Class Generator, which took the name of a WMI class and emitted an early-bound managed class wrapping it. Its options are a compact portrait of the period: /l chose the language, with CS the default and VB, MC for C++ and JS for JScript as alternatives; /n named the WMI namespace, defaulting to root\cimv2; /m named a remote machine, with /u and /pw for credentials; /o named the target .NET namespace; /p named the output file. The documented examples show the shape of the output: a Win32_ class in root\cimv2 lands in the generated .NET namespace ROOT.CIMV2.Win32, and Win32_LogicalDisk produces a class and a file named logicaldisk.

The same machinery was available inside the tools. Visual Studio .NET's Server Explorer, extended by a separately downloaded WMI extension, grew Management Classes and Management Events nodes under each server; dragging a management object onto a designer surface generated the wrapper class and a field bound to a specific object path, while a Generate Managed Class command produced the wrapper alone. Programmatically the entry point was ManagementClass.GetStronglyTypedClassCode, which returns a CodeDOM type declaration — and which is the reason the CodeLanguage enumeration, with members CSharp, JScript, VB, VJSharp and Mcpp, exists inside System.Management at all.

What the generator gave you was the code everyone wanted: a typed property access instead of a cast out of a bag, IntelliSense in the editor, a compiler that knew the difference between a UInt64 and an Int32, and static GetInstances overloads that accepted a scope, a filter and a list of properties so that a remote enumeration need not drag back everything. The type table it worked from is published and unremarkable, with one entry that matters: CIM_DATETIME maps to DateTime or TimeSpan, so the generated wrapper did the date conversion for you.

What it also gave you is stated in its own documentation, in one flat sentence: generated classes are bound to the schema for which they are generated, and if the underlying schema changes you must regenerate the class. This is where the paper over the mismatch tears. The schema changed constantly: properties were added release by release, classes present on Windows Server 2003 were absent on the Windows 2000 machine in the next room, and from the middle of the decade 64-bit Windows added a further dimension of differences for 32-bit code. The generator did not remove the run-time coupling to the repository; it converted a run-time failure on the developer's machine into a compile-time success on the developer's machine, and moved the failure to the customer's.

Two smaller frictions are documented and were themselves newsgroup material. Where a generated property or method name would collide with a standard member of the generated class, the tool renames it; where a name would be a keyword in the target language, the tool renames it too. The strongly typed wrapper is therefore not a reliable mirror of the CIM class, so the generator's naming became a subject in its own right alongside WMI's.

The generator's afterlife is a small curiosity in its own right. GetStronglyTypedClassCode is still present on ManagementClass in current .NET, and so is the CodeLanguage enumeration — complete with its VJSharp member, naming a language Microsoft stopped shipping many years ago. An API that still offers to generate wrappers in a language Microsoft has not shipped for years has outlived, by a comfortable margin, the newsgroup in which people discussed using it.

Why the managed route was slower than people expected

The expectation was natural and wrong. System.Management looks like a class library, so people costed it like one, and then discovered that a loop over a few hundred objects took a length of time that no amount of staring at the C# would explain.

The root cause is architectural and was never hidden. The namespace wraps WMI; it does not reimplement it. Every call marshals across the COM interop boundary into the same service that a script or a C++ client would reach, and the service in turn calls a provider that may live in another process. A property read is not a field access. At best it is a lookup in a bag that has already been marshalled across; at worst it is a round trip. The same fact is what makes the wrapper trustworthy — the managed client and the script are talking to exactly the same instrumentation — and what makes it expensive.

From that follow the pieces of advice that recur throughout the documentation and the traffic. Ask for the properties you need rather than for all of them: a projection reduces both what the provider must produce and what must be marshalled back. Reuse a connected ManagementScope across queries rather than constructing a fresh one each time, because a scope is a connection with an authentication handshake behind it and constructing one per query pays for that handshake per query; IsConnected and an explicit Connect() exist so that a caller can manage this deliberately.

Then there are the enumeration options, none of which appears in the four-line example everybody starts from and each of which changes what the service actually does. Rewindable, left set, means the collection keeps its objects available for repeated enumeration and therefore keeps them in memory; cleared, the collection may be walked once and is correspondingly cheaper. BlockSize governs how many objects come back per fetch. DirectRead goes straight to the provider for the named class without regard to its superclass or its derived classes. EnumerateDeep decides whether enumerating a class recurses into all classes derived from it or returns only its immediate derived members — the reference's own example sets it in order to walk every subclass of CIM_Service. UseAmendedQualifiers pulls back localisable descriptive text that most callers never read.

Remote work adds its own arithmetic. WMI reaches other machines over DCOM, and the documentation is blunt about what that entails: remote WMI connections must comply with DCOM's security requirements for impersonation and authentication. Impersonation level, authentication level, firewall configuration and the account a service runs under all sit between a query and its answer, which is one concrete reason the developer's desktop and the production server behave differently, and the managed classes do essentially nothing to simplify any of it: they surface two enumerations and otherwise get out of the way. The same COM security machinery fed a parallel stream of questions into microsoft.public.platformsdk.security; the operational half of the problem — firewall rules, DCOM configuration, a damaged repository — belongs to the administrators' page.

And events have a cost of their own: watching a high-frequency intrinsic event through a polling interval obliges the service to poll at that interval, per subscriber, whether or not anything has changed.

One limit should be stated rather than fudged. No benchmark figures survive for any of this and none is invented here. What can be stated is what the documented options do, what the documentation advised, and what the shape of the architecture makes unavoidable.

What the traffic looked like

The staple thread was schema navigation: how do I read X about this machine from C#, where X ranged over serial numbers, share permissions, installed hotfixes, battery state, monitor details and everything else a management application might conceivably want. A good answer was a class name, a short WQL query, and a warning about which property exists on which version of Windows. The third element was the one that could not be looked up.

Version drift ran underneath everything. Classes gained properties release by release, so a query developed against Windows XP could return richer data on Windows Server 2003 and nothing at all on the Windows 2000 machine in the next room; from the middle of the decade, 64-bit Windows added a further set of surprises for 32-bit code. Good answers therefore came qualified — this property from XP onwards, that method on server editions only — and the qualification was frequently exactly what the documentation had left out.

Remote administration supplied the reliably hard cases, and the genre is recognisable at a glance: code that works on the developer's desktop and returns access-denied when the same assembly runs under a service account on another machine. Untangling which of several layers had refused the connection was a recurring exercise that the managed classes did nothing to shorten.

Beneath those, the recurring shapes were consistent enough to list:

  • Marshalling surprises at the COM seam: DMTF date-time strings, embedded objects needing a cast, arrays unpacked element by element, and null where the documentation had promised a value.
  • Event watchers that never fired, fired late, fired twice, or fired on a thread the caller had not expected — usually the polling semantics of intrinsic events, occasionally a watcher about to be collected.
  • Impersonation and privilege: queries needing administrative rights or one specific privilege enabled, and the discovery that arranging this from managed code was less obvious than arranging it from a script.
  • Performance: the cost of selecting every property when one would do, of reconnecting for every query, and of watching a frequent event through a polling interval.
  • Instrumentation, in a smaller vein: publishing an application's own state into WMI, and the deployment step that makes the schema exist on the target machine.

The shape of a good thread was consistent too. It opened with a snippet — often the whole method, since WMI code is short — an exception message, and the sentence about working on one machine and not another. Diagnosis proceeded by elimination: reproduce it in wbemtest, check the namespace, check the account, check the target's operating-system version, and only then look at the managed code. The resolutions that closed such threads were rarely clever. They were precise, which in this corner of Windows was worth considerably more.

The framework versions as a timeline

Because the room's subject was a wrapper, its history is legible as a sequence of framework releases. The dates are worth setting out, because the pattern they make is not the one a reader might expect.

  • 1.0, 13 February 2002, with Visual Studio .NET. System.Management and System.Management.Instrumentation are both present from the first release. Service Pack 2, on 7 August 2002, was the first build to travel on operating-system media, as an optional component of Windows XP Service Pack 1.
  • 1.1, April 2003, the first version built into a Windows release: it shipped with Windows Server 2003. For managed WMI code that mattered less as a feature list than as a deployment fact — from Server 2003 onwards, a runtime could be assumed on the machine being managed as well as on the machine doing the managing.
  • 2.0, 27 October 2005, with CLR 2.0 and generics. System.Management did not adopt them, then or afterwards.
  • 3.0, 6 November 2006, included in Windows Vista: the same CLR 2.0 with new stacks bolted on beside it, and nothing whatever for this namespace.
  • 3.5, 19 November 2007, still on CLR 2.0, and the release that brought LINQ. LINQ's Enumerable shares an assembly — System.Core.dll — with the WMI.NET Provider Extensions attributes, which is the clearest dating evidence for those attributes that this page was able to find; the release in which they actually shipped is not asserted here.
  • 4.0, 12 April 2010, with CLR 4: released seven weeks before the newsgroup itself was deleted.
  • 4.5, 15 August 2012, included in Windows 8 and Windows Server 2012, bringing tasks and the await keyword. The observer-and-events pattern in System.Management was left exactly as it was.
  • 4.8, 18 April 2019, announced as the last major version of .NET Framework, with a final 4.8.1 on 9 August 2022.

The pattern is that almost nothing happened. Across seventeen years and eight releases, the managed WMI surface acquired one substantial addition — the provider-extension attributes — and otherwise stood still while the language and the runtime around it changed out of recognition. That is not neglect exactly. It is the predictable fate of a wrapper: the thing being wrapped did not change its shape, so the wrapper could not change its own without breaking every caller.

What replaced it, and what the guidance says now

The first change in who needed this room came from the shell rather than from the framework. Windows PowerShell 1.0, released on 14 November 2006 — developed under the codename Monad and first shown publicly at the Professional Developers Conference in October 2003 — shipped with WMI access built in, and Get-WmiObject turned the one-off query, previously a small compiled program or a script file, into a single interactive line. An administrator who might once have asked here how to enumerate services from C# no longer had a reason to. What remained was the harder residue: applications that genuinely needed WMI from managed code, inside a process that was doing something else. The succession proper — what PowerShell did to WMI practice at large — belongs to the administrators' page.

The replacement arrived from the Windows side rather than the framework side. Windows 8 and Windows Server 2012 introduced Windows Management Infrastructure, abbreviated MI, which Microsoft describes as the latest version of the WMI technologies, based on the same DMTF CIM standard and fully compatible with previous versions of WMI. For managed callers the new surface is the Microsoft.Management.Infrastructure namespace, and its vocabulary is the standard's rather than the wrapper's: CimSession where there was a ManagementScope, CimInstance where there was a ManagementObject, plus CimClass, CimProperty, CimMethodDeclaration, CimMethodParameter, CimMethodResult, CimQualifier, CimSubscriptionResult, CimSystemProperties, a CimException and a CimType of its own.

The same layer sits beneath PowerShell's CIM cmdlets, which arrived with PowerShell 3.0 — released to manufacturing in August 2012, generally available in Windows Management Framework 3.0 in December 2012, and included in Windows 8 and Windows Server 2012. The module supplies Get-CimInstance, Get-CimClass, Get-CimAssociatedInstance, Invoke-CimMethod, New-CimInstance, Set-CimInstance, Remove-CimInstance, the session cmdlets and Register-CimIndicationEvent for subscriptions. Microsoft's own reference page for the older Get-WmiObject now opens by stating that starting in PowerShell 3.0 the cmdlet has been superseded by Get-CimInstance. The administrators' side of that succession is told on the Windows XP page and not here.

The wire changed with it. The classic interfaces remote over DCOM; the CIM stack remotes over WS-Management, the SOAP-based protocol Microsoft pushed with Intel, Sun Microsystems and Dell and that Windows has carried since Windows Server 2003 R2. For anyone who adopted the new stack, that retired the entire proxy-and-firewall genre of failure which filled so much of this room's traffic. For anyone who did not, it retired nothing at all, which is why the genre survives in support forums to this day.

Current guidance is not ambiguous, and it is printed at the head of the reference entries for the affected types. Microsoft's documentation for the older WMI classes carries a standing notice: the WMI .NET libraries are now considered in a final state, with no further development, enhancements or updates available for non-security issues affecting them, and the MI APIs should be used for all new development. Final state is not deprecation — the libraries are still shipped, still supported and still fixed for security — but it is a clear line. The advice that was current when this room was busy, which was to use System.Management and generate a strongly typed wrapper if types mattered to you, is now advice for maintaining existing code rather than for writing new.

Afterlives on the managed side

System.Management outlived .NET Framework, but not intact, and the details are worth stating carefully because this is a place where a confident wrong sentence is easy to write.

When the rewritten cross-platform runtime arrived without the namespace, the gap was conspicuous enough that the package name was claimed by a placeholder: NuGet's System.Management version 4.0.0 carries, as its entire description, the words “Unsupported libraries System.Management.dll & System.Management.Instrumentation.dll”. Previews of a real port followed from November 2017, and the package has shipped continuously since; the version current as this page was written was published in August 2026, numbered in step with the runtime.

The first thing that did not travel is portability, and it is not an accident of packaging. The runtime's own build compiles the library only for Windows targets and, for any target platform that is not Windows, generates a stand-in assembly whose members throw, carrying the message: “System.Management currently is only supported for Windows desktop applications.” The package restores on Linux and macOS and the code compiles there; it fails when it runs. The reason is not reluctance but arithmetic: WMI is a Windows service reached through COM, and there is nothing on another operating system for the wrapper to wrap. PowerShell's own CIM cmdlet module carries the identical restriction, noting that it is available only on the Windows platform.

The second thing that did not travel is the publishing half. The reference assembly for System.Management in current .NET declares exactly one namespace — System.Management — and stops there. System.Management.Instrumentation, in both of its models, exists only on .NET Framework. A modern .NET application can read WMI on Windows perfectly well; to publish its own state into WMI it must either target .NET Framework or go to the MI provider surface instead. A handful of the surviving types carry obsolescence warnings on their serialisation members, pointing at the formatter-based serialisation that modern .NET has been retiring.

What did survive, oddly, includes the ornaments. GetStronglyTypedClassCode is still on ManagementClass, the CodeLanguage enumeration still lists Visual J# among its output languages, and ManagementDateTimeConverter still converts the same fixed-width strings it converted in 2002. The library is a working museum of the decisions this newsgroup spent a decade explaining.

The subject outlived everything else in the story by a comfortable margin. WMI remains load-bearing in Windows; the wmic command-line client was deprecated as of Windows 10 version 21H1 and the 21H1 release of Windows Server, is no longer installed by default in Windows 11 versions 24H2 and 25H2, and from August 2026 can no longer be added back to them even as an optional feature — while the service beneath it carried on unchanged, and Microsoft's notice says so in as many words: the deprecation applies to the command-line utility, and WMI itself is not affected. Two decades after the group opened, a developer can still write the same four lines, get the same boxed UInt64 out of the same string-keyed bag, and need the same explanation.

Why the citations point here

Newsgroup threads about a narrow API have a property that outlasts their venue: they contain working code attached to a described problem. A thread that begins with a mangled DMTF date-time and ends with a correct conversion routine is a reference document whatever medium happens to preserve it, and the web of the 2000s treated it as one. Practitioner pages, consultants' link collections, course reading lists and tutorial authors filling gaps in the official documentation pointed at archived threads and at the group name itself, and search engines did the rest: once two decades of Usenet became full-text searchable, an exception message pasted into a search box led as readily to a newsgroup thread as to anything official.

There is also a structural reason particular to this address, and it is visible in the record examined earlier. When Microsoft closed the hierarchy, it mapped this group not to a successor forum but to a category index. Nothing more specific was ever offered. A citation made in 2006 to microsoft.public.dotnet.framework.wmi therefore did not acquire a better target in 2010, or afterwards, because no room devoted to managed WMI was ever opened to receive it. The old name remained the most precise address anyone could give for the subject, which is exactly the condition under which an obsolete pointer keeps being copied forward.

The pattern is worth stating without romance. The group was not a curated knowledge base. It was a conversation, with wrong answers, dead ends and a great deal of repetition. But its good threads were written by people debugging an actual problem on an actual machine, sometimes with a Microsoft engineer or an MVP in the room, and that specific texture — the failed attempt shown beside the fix — is what the polished documentation of the period lacked and what the citing pages were pointing their readers at.

What this page cannot do is audit those citations. It does not know which sites link here, in what numbers, or how many still resolve, and it invents no figures. What it can say is that the mechanism is ordinary, well documented and still operating, and that the archives which make it work remain open.

Neighbours and answerers

Answers came from the mix that made Microsoft's news server distinctive: fellow practitioners, Microsoft engineers who watched their own products' rooms, and the MVPs whose recognition programme grew out of exactly this kind of unpaid expertise. That culture, and the hierarchy's own history, are described at length on the page for microsoft.public.word.numbering.

The division of labour among the WMI rooms was by audience, and it held reasonably well. microsoft.public.win32.programmer.wmi took the COM interfaces, the schema language and the writing of providers. microsoft.public.windowsxp.wmi took the administrators, their scripts and their broken machines. microsoft.public.scripting.vbscript took the language in which most of the world's WMI was actually written, and by which most of this group's readers had first encountered the technology before being asked to do the same thing in C#. This room took the people holding a managed object. The fourth WMI room in the hierarchy, microsoft.public.wmi.programmer, sat directly beneath the stem and is not preserved in this directory.

Cross-posting among the set was common and, given how often a question's real home only became clear after diagnosis, forgivable. Steering a thread to the right room was part of the regulars' unpaid workload, and the standard elimination move — try it in wbemtest — was as much a routing decision as a diagnostic one.

What the record does not show

Several things a reader might reasonably want are simply not available, and it is better to list them than to fill the gaps.

  • The message archive of this group is not preserved on this site. This page describes the room, its subject and its world; it does not reproduce its posts.
  • No traffic figure is claimed. There is no reliable count of articles, threads, posters or readers for this group in any source consulted, and none is estimated here.
  • No thread title, no poster's name, no quotation from a post and no message count appears anywhere on this page, because none could be verified. The one quotation from Usenet above is from an archived control message, not from a discussion.
  • The group's creation date is unknown. No control message for it survives in the ISC archive, which does hold messages for many of its siblings, and this page does not infer a date from the siblings' timestamps.
  • The relative volume of the four WMI rooms is unknown, as is how much of this room's traffic concerned instrumentation rather than querying. The characterisation above reflects the shape of the surviving documentation and tooling, not a count.
  • The exact framework release in which the WMI.NET Provider Extensions first shipped could not be established from a source this page would rely on. The attributes are documented as living in System.Core.dll rather than System.Management.dll, which places them later than the original instrumentation model, and that is as far as the evidence goes.

Scope and limits of this page

This page is about a newsgroup and the technical world it sat in. It is written from the surviving standard record — the mirrored active file and control archive, an archived copy of Microsoft's own newsgroup migration page — together with the published reference documentation for the APIs the room discussed, the source and package history of the current .NET libraries, Microsoft's own deprecation notices, and one period magazine article. Every type name, enumeration member, tool option and version date above was checked against those sources while this page was written.

Where a claim could not be checked it is not made. Where the room's character is described rather than counted, that is a characterisation of a well-documented technical situation and not a summary of posts nobody here has read. And where a topic belongs to a neighbouring page — the native API and providers, the schema language, the administrator's toolbox, the scripting idiom, the operational failures, the hierarchy's own statistics and its closure — it has been pointed at rather than retold, which is the arrangement this directory uses to keep three pages about one technology from becoming three copies of the same page.

Reading microsoft.public.dotnet.framework.wmi today