Using WMI/CIM from C# and PowerShell — A Practical Guide to Hardware Inventory, Process Monitoring, and Remote Queries
· Updated: · Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, Business Applications, Windows Development
Revision history (first version, published Aug 1, 2026)
- First published
Cite this article(DOI (registered archive): 10.5281/zenodo.22170841)
The DOIs below refer to previously archived versions and may not match the current text. Use this page’s URL to reference the current text.
Go Komura (2026). Using WMI/CIM from C# and PowerShell — A Practical Guide to Hardware Inventory, Process Monitoring, and Remote Queries. KomuraSoft LLC. https://comcomponent.com/en/blog/wmi-cim-practical-guide/
- DOI (registered archive)
- 10.5281/zenodo.22170841
- DOI (last registered version)
- 10.5281/zenodo.22170842
“I want to display the PC’s serial number and model name.” “I want to check a server’s free disk space.” “I want to detect when a process starts.” In Windows business applications and management tools, WMI/CIM is what lets you handle information like this through one common mechanism.
CIM is the standard model for representing management information, and WMI is the Windows management infrastructure that uses that standard. PowerShell’s CIM cmdlets and the C# APIs are the entry points to that infrastructure.1
flowchart TB
accTitle: Common Requirements and WMI/CIM
accDescr: Displaying the serial number and model name, monitoring free disk space, detecting process starts, and querying remote PCs are standard business application requirements whose standard answer is WMI, which uses the CIM standard to represent management information
r1["Serial number and model name"] --> ans["WMI (infrastructure that uses the CIM standard)"]
r2["Monitoring free disk space"] --> ans
r3["Detecting process starts"] --> ans
r4["Querying remote PCs"] --> ans
Figure 1: WMI/CIM is the standard answer to four requirements that come up constantly in business applications.
What makes this confusing is that there is more than one entry point. Search results mix the old Get-WmiObject with Get-CimInstance, and C# has both System.Management and Microsoft.Management.Infrastructure. The old WMI cmdlets still work in Windows PowerShell 5.1, but they do not exist in PowerShell 7.2
This article is for C#/PowerShell developers implementing hardware information retrieval, process monitoring, and remote PC queries. It goes in this order: decide whether WMI is the right tool, try it in PowerShell, check the connection requirements, build it into C#, and then round it out with monitoring and troubleshooting. Examples and cautions are organized from primary sources current as of August 2026.
1. The Bottom Line: Pick the Entry Point from the Use Case and the Target
Write new PowerShell with the CIM cmdlets, and pick the C# API to match the use case. That said, there is no need to push work onto WMI when a purpose-built mechanism already covers it.
| Decision or task | Basic approach | Section that covers it |
|---|---|---|
| Decide whether to use WMI | Use it for cross-cutting information retrieval and remote queries; choose a purpose-built mechanism for settings and high-frequency performance monitoring | Section 2 |
| Decide what to query | Pick the namespace, class, and properties, and narrow the target with WQL. The day-to-day default is root/CIMV23 |
Sections 3 and 4 |
| Try it in PowerShell | Read with Get-CimInstance and act with Invoke-CimMethod. Write new code on the CIM side even when it targets 5.12 |
Section 4 |
| Extend it to remote machines | Default to WSMan/WinRM, and reuse a CIM session for multiple operations against the same target. Choose DCOM when you have to34 | Section 5 |
| Build it into C# | Use System.Management for quick local queries, and consider the MI API when you are building in remote queries or monitoring56 |
Section 6 |
| Monitor process starts | Use an event subscription, and design for privileges, unsubscribing, and re-registering after a disconnect78 | Section 7 |
| Find out why it is slow, returns the wrong value, or cannot find a class | Isolate the volume and frequency of queries, the provider’s bitness, and repository consistency | Section 8 |
In particular, being able to enumerate something and being able to subscribe to its events are two different things. The Win32_ProcessStartTrace subscription example runs with administrator rights.7 And do not reach for SELECT * or short-interval polling out of habit: narrow the result down to the information you need with -Filter, -Property, and -KeyOnly.3
In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (26 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle
2. When to Use WMI/CIM and When to Choose a Purpose-Built Mechanism
WMI is excellent as a unified read interface, but it is not always the best answer. Here are the practical guidelines for telling the two cases apart.
| What you want to do | Suitable mechanism | Why not WMI |
|---|---|---|
| Retrieving hardware information and OS configuration, and agentless remote queries | WMI/CIM | This is where WMI is at its best. It is more uniform than calling individual dedicated APIs |
| Reading and writing your own application’s settings | Reading the registry directly (Microsoft.Win32.Registry) or a configuration file |
Touching the registry through WMI is a detour, and it drags in the bitness problem from Section 8.2 |
| High-frequency, continuous performance monitoring such as CPU usage | Performance counters (System.Diagnostics.PerformanceCounter and similar) |
Counters exist for exactly this. Short-interval WMI polling loses on both load and accuracy |
| One-off OS function calls, and processing that needs low latency | The Win32 API (P/Invoke) | WMI carries the overhead of going through COM and a provider |
| Local process enumeration and control that your own process’s privileges already cover | System.Diagnostics.Process |
It is self-contained in the standard library, so you carry fewer dependencies |
| Configuring Windows management features such as the firewall and networking | Purpose-built CIM-based cmdlets such as Get-NetFirewallRule |
Cmdlet sets curated per task are more accurate and safer than hunting for raw WMI classes |
| Detecting file and folder changes | FileSystemWatcher |
Do not bring WMI into an area that already has a dedicated API |
The deciding principle is simple: use the purpose-built mechanism in areas that have one, and use WMI/CIM for cross-cutting queries and remote queries. The Get-NetFirewallRule family in the table is itself a set of cmdlets built on top of CIM, which you could describe as getting the benefit of WMI/CIM without touching it directly.
flowchart TB
accTitle: The Deciding Principle
accDescr: The deciding principle is to use the purpose-built mechanism in areas that have one and to use WMI and CIM for cross-cutting queries and remote queries in areas that do not, while purpose-built CIM-based cmdlets are a way of getting the benefit without touching WMI and CIM directly
q1{"Is there a purpose-built mechanism?"} -->|Yes| ded["Use the purpose-built mechanism"]
q1 -->|No| wmi["Use WMI / CIM"]
wmi -.-> use["Cross-cutting queries and remote queries"]
cmd["Purpose-built CIM-based cmdlets"] -.-> ben["Get the benefit only"]
Figure 2: Use the purpose-built mechanism where one exists, and use WMI/CIM for cross-cutting and remote queries.
3. Getting the Mechanism Straight: The Standard, Namespaces, Classes, and WQL
3.1. CIM Is the Standard, WMI Is the Implementation on Windows
First, sort out how the terms relate, once and for all.
| Term | What it actually is |
|---|---|
| CIM (Common Information Model) | The industry-standard model for representing management targets such as systems, applications, networks, and devices. Defined and maintained by the DMTF (Distributed Management Task Force)1 |
| WBEM (Web-Based Enterprise Management) | An industry initiative that creates standard technologies for accessing management information in enterprise environments1 |
| WMI | Microsoft’s implementation of WBEM. It uses the CIM standard to represent management targets and is built into Windows1 |
| MI (Windows Management Infrastructure) | The next-generation version of WMI. Fully compatible with classic WMI, and most new providers are written for MI1 |
flowchart TB
accTitle: How the CIM Standard Relates to the WMI Implementation
accDescr: The CIM standard defined and maintained by the DMTF is used within the framework of the WBEM initiative, WMI is the Microsoft implementation of it, the next-generation MI is fully compatible with classic WMI, and CIM-family APIs all connect to the same WMI infrastructure
dmtf["Defined and maintained by the DMTF"] --> cim["CIM (industry-standard model)"]
wbem["WBEM (industry initiative)"] --> wmi["WMI (Microsoft implementation)"]
cim --> wmi
wmi -.-> mi["MI (next generation, fully compatible)"]
api["CIM-family APIs (PowerShell / C#)"] --> wmi
Figure 3: CIM is the specification and WMI is the implementation on Windows. CIM-family APIs all connect to the same WMI infrastructure.
3.2. Reading Namespaces, Classes, Providers, and WQL as One Chain
As a developer, there are four pieces of structure you need to have straight.
- Namespace: a hierarchy that groups classes. Day-to-day queries almost always use root/CIMV2, which is also the default for the CIM cmdlets.3 Others include
root\default(the registry provider and so on). - Class: a type of management target, such as
Win32_ComputerSystem(the computer itself),Win32_LogicalDisk(a logical drive), orWin32_Process(a process). Windows-specific classes that derive from CIM standard classes (CIM_LogicalDiskand the like) carry theWin32_prefix.9 - Provider: the component that supplies the actual instances of a class. When you issue a query, the provider asks the OS on the spot and produces the values.
- WQL: a query language similar to SQL. You treat a class as a table and narrow it down, as in
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto'. WQL is also the default query language for the CIM cmdlets.3
Being able to read OS and hardware information through a unified set of classes and one query language — that is the value of WMI. Not every class supports writing or operations, though. Classes that have methods are driven with Invoke-CimMethod, and changes to writable properties are made with Set-CimInstance. As the mapping table in Section 4.1 shows, you pick the entry point to match what the class offers.
flowchart TB
accTitle: The Structure of a WMI Query
accDescr: A WQL query is aimed at a Win32 class inside the root/CIMV2 namespace, the provider that supplies the actual instances of the class asks the OS on the spot and produces the values, and the result is returned
wql["Query with WQL"] --> ns["Namespace root/CIMV2"]
ns --> cls["Win32_* class"]
cls --> prov["Provider"]
prov --> osq["Asks the OS on the spot"]
osq --> res["Returns the result"]
Figure 4: A query follows namespace, then class, then provider, and the values are produced on the spot.
4. Trying It in PowerShell: Reading, Calling Methods, and Asset Information
From here on, try things in PowerShell on Windows. Even when porting old code, start by confirming how the cmdlets map to each other and how the return values differ.
4.1. Write New Code with the CIM Cmdlets
In PowerShell 6 and later (the current PowerShell 7), the following WMI v1 cmdlets have been removed. The same functionality is provided by the CimCmdlets module (WMI v2).2
| Old (through Windows PowerShell 5.1) | Current (CIM cmdlets) | Notes |
|---|---|---|
Get-WmiObject |
Get-CimInstance |
The idea behind -Filter / -Query is the same |
Get-WmiObject -List |
Get-CimClass |
Discovering classes and checking their definitions |
Invoke-WmiMethod |
Invoke-CimMethod |
Arguments are passed as a hash table with -Arguments @{ } |
Register-WmiEvent |
Register-CimIndicationEvent |
Event subscription (Section 7) |
Set-WmiInstance |
Set-CimInstance |
Changing writable properties |
Remove-WmiObject |
Remove-CimInstance |
Deleting an instance |
The CIM cmdlets work in Windows PowerShell 5.1 as well, so writing anything new on the CIM side, even when it will run under 5.1, is the way to avoid leaving migration cost behind. The full picture of running 5.1 and 7 side by side and migrating between them is covered in “Differences Between Windows PowerShell 5.1 and PowerShell 7”.
flowchart TB
accTitle: Why New Scripts Should Be Written with CIM
accDescr: A script written with the WMI cmdlets runs under 5.1 but has been removed in PowerShell 6 and later so it needs rewriting at migration time, while the CIM cmdlets also work under 5.1, so writing anything new on the CIM side leaves no migration cost behind
new["A new script"] --> q1{"Which one do you write it with?"}
q1 -->|WMI cmdlets| old["Runs under 5.1"]
q1 -->|CIM cmdlets| cur["Works under 5.1 too"]
old --> del["Removed in PowerShell 7"]
del --> rew["Rewrite at migration time"]
cur --> norew["No migration cost left behind"]
Figure 5: Write new code with the CIM cmdlets and you will not have to rewrite it when you migrate to PowerShell 7.
4.2. Narrow Rows and Columns with Get-CimInstance
# Specify the class (default namespace root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem
# Put only the WHERE clause in -Filter (do not write the WHERE keyword)
Get-CimInstance -ClassName Win32_Service -Filter "StartMode = 'Auto' AND State <> 'Running'"
# Retrieve only the properties you need to cut down the amount transferred
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, CreationDate
# Use -Query if you want to write the WQL yourself
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name LIKE 'p%'"
-Filter is the WQL WHERE clause itself, and -Property limits which columns you retrieve.3 The return value is a CimInstance object, and date properties (CreationDate, LastBootUpTime, and so on) come back already converted to DateTime.
4.3. Call Methods with Invoke-CimMethod
Unlike the old Get-WmiObject, you do not call WMI methods directly on the object you retrieved, so method calls go through Invoke-CimMethod.
flowchart TB
accTitle: Calling a Method on a CimInstance
accDescr: The CimInstance returned by Get-CimInstance comes back with date properties already converted to DateTime, but it is not a form on which you call WMI methods directly, so a method call is made by passing the instance to Invoke-CimMethod
gci["Get-CimInstance"] --> inst["CimInstance object"]
inst -.-> dt["Dates already converted to DateTime"]
inst -.-> nom["WMI methods go through another entry point"]
inst --> icm["Pass it to Invoke-CimMethod"]
icm --> call["Method call"]
Figure 6: Do not call a CimInstance’s WMI methods directly; make method calls by passing the instance to Invoke-CimMethod.
# Call a method on an instance: get the owner of each process
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
Invoke-CimMethod -MethodName GetOwner
# Call a static method on the class: start a process
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }
# Inspect the class definition (the list of properties and methods)
Get-CimClass -ClassName Win32_Process
4.4. Choose the Class from the Information You Want
| Information you want | Class | Main properties |
|---|---|---|
| Manufacturer and model name | Win32_ComputerSystem |
Manufacturer, Model |
| Chassis serial number | Win32_BIOS |
SerialNumber |
| OS edition and boot time | Win32_OperatingSystem |
Caption, Version, LastBootUpTime |
| Free disk space | Win32_LogicalDisk |
DeviceID, FreeSpace, Size, DriveType9 |
| Service state | Win32_Service |
Name, State, StartMode |
| Process list | Win32_Process |
Name, ProcessId, CommandLine |
4.5. An Asset Management Example: Model, Serial Number, and Free Disk Space
# Model information and serial number (for reconciling a PC asset register)
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -Property Manufacturer, Model
$bios = Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber
[pscustomobject]@{
Manufacturer = $cs.Manufacturer
Model = $cs.Model
Serial = $bios.SerialNumber
}
# Free space on local disks (DriveType = 3)
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" |
Select-Object DeviceID,
@{ Name = 'FreeGB'; Expression = { [math]::Round($_.FreeSpace / 1GB, 1) } },
@{ Name = 'SizeGB'; Expression = { [math]::Round($_.Size / 1GB, 1) } }
DriveType = 3 is the value that means “local disk”, and it excludes removable drives (2), network drives (4), and CDs (5).9 Once you satisfy the connection requirements in Section 5, running this script against each server through a CIM session gives you the foundation for agentless disk monitoring.
flowchart TB
accTitle: The Foundation for Agentless Disk Monitoring
accDescr: Filtering on DriveType 3 excludes removable drives, network drives, and CDs so that only local disks are covered, and running the same script against each server through a CIM session gives you the foundation for agentless disk monitoring
scr["Free-space retrieval script"] --> flt["Filter on DriveType = 3"]
flt -.-> exc["Excludes removable drives and the like"]
scr --> ses["Through a CIM session"]
ses --> srvs["Run it against each server"]
srvs --> mon["Agentless monitoring"]
Figure 7: Running a script narrowed to local disks against each server over a CIM session is the foundation for monitoring.
5. Extending to Remote Machines: Connection Methods and Prerequisites
5.1. The Connection Method Changes Between Local and Remote
A CIM cmdlet that is not given -CimSession connects to local WMI over COM if -ComputerName is not specified either, and creates a temporary session over the WSMan (WinRM) protocol when -ComputerName is specified. If you are performing several operations against the same computer, creating a CIM session and reusing it performs better.3
flowchart TB
accTitle: Choosing a CIM Connection Method
accDescr: Without CimSession, omitting ComputerName connects to local WMI over COM while specifying ComputerName creates a temporary WSMan session on every query, reusing New-CimSession performs better for multiple operations against the same target, and a DCOM protocol option is available for targets where WinRM is not configured
exec["Run without passing CimSession"] --> q1{"Is ComputerName specified?"}
q1 -->|No| local["COM connection to local WMI"]
q1 -->|Yes| q2{"Several operations on the same target?"}
q2 -->|One-off| temp["Temporary WSMan session"]
q2 -->|Several| sess["Reuse New-CimSession"]
temp -.-> cost["Created on every query"]
nowinrm["Target without WinRM configured"] -.-> dcom["DCOM protocol option"]
Figure 8: Remote queries default to WSMan, and reusing a CIM session is the established practice for several operations against the same target.
5.2. Prepare WinRM, the Firewall, Authentication, and Privileges
The prerequisites for querying over WSMan/WinRM are as follows. Before you write any connection code, check the service, the network path, authentication, and privileges separately.
- WinRM must be configured on the target.
winrm quickconfigdoes all of it at once: setting the service to start automatically, creating an HTTP listener (default port 5985), and registering a firewall exception.10 If you want to connect over HTTPS (default port 5986), that alone is not enough: you have to prepare a server certificate and then configure an HTTPS listener separately with something likewinrm quickconfig -transport:https.10 - The relevant ports must be open in the firewalls along the path. Designing and registering inbound rules in practice works exactly as covered in the article on Windows Firewall and business applications.
- Authentication. In a domain environment, Kerberos provides mutual authentication. Kerberos is not available in a workgroup, so you may need to register the target in the client’s
TrustedHostslist. Keep that list to the absolute minimum.10 - Privileges. With the default configuration, remote WMI queries and operations are normally performed with an account that belongs to the administrators group on the target. If you want to open this up to standard users, you have to configure access permissions in both WinRM and the WMI namespace.10
flowchart TB
accTitle: Checking the Prerequisites for Remote Queries
accDescr: On the target, winrm quickconfig sets the service to start automatically and creates an HTTP listener and registers a firewall exception all at once, an HTTPS listener is configured separately after preparing a certificate, and in a workgroup environment registration in TrustedHosts may be required
qc["winrm quickconfig"] --> svc["Service set to start automatically"]
qc --> lis["HTTP listener created (5985)"]
qc --> fw["Firewall exception"]
lis ~~~ https["HTTPS listener (5986)"]
https -.-> cert["Prepare a certificate and configure separately"]
fw ~~~ wg["Workgroup environment"]
wg -.-> th["Register only where necessary"]
Figure 9: winrm quickconfig applies the default configuration in one step; the HTTPS listener and workgroup authentication are handled separately.
5.3. Reuse a CIM Session for Several Operations Against the Same Target
# For a one-off, use -ComputerName (a temporary session is created each time)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02
# For repeated queries, reuse a CIM session
$session = New-CimSession -ComputerName Server01
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $session
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" -CimSession $session
Remove-CimSession $session
5.4. Consider DCOM for Targets Where WinRM Cannot Be Configured
For targets you cannot reach over WSMan, such as older machines where WinRM cannot be configured, you can choose the DCOM protocol.4
$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom
DCOM also uses dynamic RPC ports, which makes designing anything that crosses a firewall harder. For anything you are building now, treating WSMan as the default is the safer assumption.
The session examples show how to connect. When you build this in, guarantee cleanup with try / finally or similar so that Remove-CimSession is reached even if a query fails partway through. The session created in the DCOM example is released the same way.
6. Building It into C#: Two APIs and How They Handle Types and Dates
6.1. Choose Between the Two APIs by Use Case
There are two API lineages for using WMI from C#. Both are Windows-only.
| System.Management | Microsoft.Management.Infrastructure (MI API) | |
|---|---|---|
| How to get it | Built into .NET Framework. On current .NET, the NuGet package System.Management5 | The NuGet package Microsoft.Management.Infrastructure6 |
| Entry class | ManagementObjectSearcher (pass it WQL to query)5 |
CimSession (Create, then QueryInstances / InvokeMethod / Subscribe)6 |
| Type system | ManagementObject / ManagementEventWatcher11 |
CimInstance / CimSession — the same types as the CIM cmdlets3 |
| Remote | DCOM-based | WSMan-based (CIM sessions). Asynchronous variants (*Async) available6 |
| Where it fits | Local information retrieval. Maintaining an existing code base | Building in remote queries and monitoring. Designs that also use PowerShell |
6.2. System.Management: Pass It WQL and Read Along the CIM Types
Pass the WQL as a string and receive the result collection from Get().5
// NuGet: System.Management (Windows-only)
using System.Management;
using var searcher = new ManagementObjectSearcher(
@"root\cimv2",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (ManagementObject disk in searcher.Get())
{
var freeGb = (ulong)disk["FreeSpace"] / 1024.0 / 1024.0 / 1024.0;
var sizeGb = (ulong)disk["Size"] / 1024.0 / 1024.0 / 1024.0;
Console.WriteLine($"{disk["DeviceID"]} free {freeGb:F1} GB / total {sizeGb:F1} GB");
}
Properties come back through the indexer as object, so check the CIM type in the class documentation (here FreeSpace and Size are uint649) and cast accordingly. Casting to int because you assumed that is the type, without checking, gives you an InvalidCastException — that is the classic first stumble.
flowchart TB
accTitle: Retrieving Properties and the Casting Pitfall
accDescr: System.Management properties come back through the indexer as object, so you have to check the CIM type in the class documentation before casting, and casting on the assumption that it is an int gives you an InvalidCastException
idx["Retrieved through the indexer"] --> obj["Comes back as object"]
obj --> chk["Check the CIM type in the documentation"]
chk --> cast["Cast to the correct type"]
obj -.-> wrong["Cast assuming it is an int"]
wrong -.-> ex["InvalidCastException"]
Figure 10: Properties come back as object, so check the CIM type before casting.
6.3. The MI API: Query with the Same Types as PowerShell
CimSession handles local and remote the same way. Enumeration, queries, method calls, event subscription, and asynchronous variants are all there.6
// NuGet: Microsoft.Management.Infrastructure (Windows-only)
using Microsoft.Management.Infrastructure;
// CimSession.Create(null) for local; pass a computer name for remote
using CimSession session = CimSession.Create(null);
IEnumerable<CimInstance> disks = session.QueryInstances(
@"root\cimv2", "WQL",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (CimInstance disk in disks)
{
var deviceId = (string)disk.CimInstanceProperties["DeviceID"].Value;
var free = (ulong)disk.CimInstanceProperties["FreeSpace"].Value;
Console.WriteLine($"{deviceId} free {free / 1024.0 / 1024 / 1024:F1} GB");
}
Because you handle the same CimInstance that PowerShell’s CIM cmdlets return, the development flow of “try it in PowerShell, then transcribe it into C#” connects straightforwardly. If you are designing the C#-to-PowerShell integration itself, see “Running PowerShell from C# and Receiving the Results as Objects” as well.
flowchart TB
accTitle: Prototyping in PowerShell and Transcribing into C#
accDescr: Because PowerShell CIM cmdlets and the C# MI API both handle the same CimInstance type, the development flow of prototyping in PowerShell and then transcribing into C# connects straightforwardly
trial["Prototype in PowerShell"] --> gci["CIM cmdlets"]
impl["Real implementation in C#"] --> mi["MI API"]
gci --> ci["The same CimInstance type"]
mi --> ci
ci -.-> flow["Transcribing connects straightforwardly"]
Figure 11: The CIM cmdlets and the MI API handle the same CimInstance type, so a prototype carries over into the real implementation.
6.4. Do Not Slice DMTF Dates by Hand; Hand Them to the Conversion API
WMI dates are stored as strings in the DMTF format from the CIM specification, yyyymmddHHMMSS.mmmmmm±UUU (the trailing value is the offset from UTC in minutes; for example 20260801100000.000000+540). Stop slicing the raw value with string operations and use a conversion API instead.
- C# (System.Management):
ManagementDateTimeConverterprovides conversion both ways between the DMTF format andDateTime/TimeSpan.11 - CIM-family APIs (Get-CimInstance / the MI API): date properties come back already converted to
DateTime, so you never run into the problem in the first place.(Get-CimInstance Win32_OperatingSystem).LastBootUpTimecan be used in calculations as aDateTimedirectly.
flowchart TB
accTitle: Handling the DMTF Date Format
accDescr: WMI dates are stored as strings in the DMTF format, so a raw value read through System.Management is converted with ManagementDateTimeConverter while a CIM-family API returns it already converted to DateTime, and you never slice the string yourself
dmtf["DMTF-format string"] --> q1{"Which API retrieved it?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|CIM-family API| done["Returned already converted to DateTime"]
conv --> dtv["Convert to DateTime / TimeSpan"]
cut["Slicing the string yourself"] -.-> ng["Do not do this"]
Figure 12: Leave DMTF string conversion to the conversion API, and with a CIM-family API just use the DateTime you are given.
The code here shows the basic shape of each API. In a real application you also have to handle retrieval failures and unset values, and dispose of result collections, retrieved objects, and sessions in whatever way the API you use requires.
7. Monitoring Process Starts: Privileges, Subscribing, Unsubscribing, and Re-Registering
Rather than “poll Win32_Process at intervals and diff the results”, use an event subscription. For process starts, the simplest approach is to subscribe to Win32_ProcessStartTrace (an event class from the kernel trace provider, with properties such as ProcessName, ProcessID, and ParentProcessID8).
7.1. Decide the Execution Privileges and Where the Monitoring Lives First
Register-CimIndicationEvent registers a subscription by class name or WQL event query, and the script block in -Action runs every time an indication arrives.7 Subscribing to this class requires administrator rights.7 Who can receive an event is controlled by the event class’s security descriptor, and a standard user gets access denied.8
Subscriptions to the Win32_ProcessStartTrace family assume administrator rights.7 “It worked on the development machine, where we ran as administrator, but monitoring does not work in the customer’s standard-user environment” is as classic a failure as the firewall notification dialog. If you are building monitoring into a business application that runs as a standard user, consider splitting the monitoring part into a Windows service (running as LocalSystem or similar) and connecting it to the application itself through interprocess communication.
flowchart TB
accTitle: A Monitoring Layout for Standard-User Environments
accDescr: A subscription that requires administrator rights is carved out of the application itself which runs as a standard user, the monitoring part is split into a Windows service running as LocalSystem or similar, and the two are connected through interprocess communication
svcm["Windows service for monitoring"] --> subm["Subscribes to the start trace"]
svcm -.-> lsm["Runs as LocalSystem or similar"]
appm["The application itself (standard user)"] ---|Interprocess communication| svcm
Figure 13: Split a subscription that needs administrator rights into the service side and connect it to the application through interprocess communication.
7.2. Subscribe in PowerShell and Unsubscribe When Monitoring Ends
# Run this in an elevated PowerShell session
$action = {
$name = $Event.SourceEventArgs.NewEvent.ProcessName
$id = $Event.SourceEventArgs.NewEvent.ProcessID
Write-Host "Process started: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
-SourceIdentifier ProcessStarted -Action $action
The subscription is tied to the PowerShell session that registered it, and as long as it keeps running normally, -Action runs every time a process starts. Be careful not to run the unsubscribe command straight afterward in one go: all that does is remove the subscription before monitoring ever begins.
Unsubscribe when you are finished monitoring.
# When monitoring ends: remove the subscription
Unregister-Event -SourceIdentifier ProcessStarted
sequenceDiagram
accTitle: The Flow of a Process Start Event Subscription
accDescr: An elevated PowerShell session registers a subscription with Register-CimIndicationEvent, an event arrives every time a process starts and the Action runs, and the subscription is removed with Unregister-Event when monitoring ends
participant ps as PowerShell session
participant wmi as WMI
ps->>wmi: Register the subscription with Register-CimIndicationEvent
Note over ps: Run with administrator rights
wmi-->>ps: An event arrives every time a process starts
ps->>ps: Run -Action
ps->>wmi: Remove it with Unregister-Event (when monitoring ends)
Figure 14: A subscription is tied to the session that registered it, and you unsubscribe when monitoring ends.
7.3. The Generic Event That Uses WITHIN Is Polling
The other approach is the generic instance creation event (__InstanceCreationEvent), which watches for instances of a class being created. Here WMI polls at the interval you give with WITHIN and turns the differences into events, so the trade-off between detection latency and load is yours to set.
flowchart TB
accTitle: Two Subscription Methods for Detecting Process Starts
accDescr: Win32_ProcessStartTrace is a method that subscribes to an event class from the kernel trace provider, while the generic __InstanceCreationEvent has WMI poll at the interval given with WITHIN and turn the differences into events, so the trade-off between detection latency and load is yours to set
goal["Detecting process starts"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["Subscribing to a kernel trace"]
t2 -.-> w1["Polls at the WITHIN interval"]
w1 -.-> tr["Trade-off between interval and load"]
Figure 15: Either subscribe to a dedicated event class, or use the generic instance creation event with a polling interval.
# Watch for new Win32_Process instances by polling at 5-second intervals
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"
Register-CimIndicationEvent -Query $query -SourceIdentifier ProcPoll -Action {
Write-Host "Started: $($Event.SourceEventArgs.NewEvent.TargetInstance.Name)"
}
7.4. In C#, Receive Events with ManagementEventWatcher
In C# (System.Management), ManagementEventWatcher plays the same role.11
using System.Management;
// In a process running as administrator
var watcher = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
watcher.EventArrived += (_, e) =>
{
var name = (string)e.NewEvent["ProcessName"];
var pid = (uint)e.NewEvent["ProcessID"];
Console.WriteLine($"Process started: {name} (PID={pid})");
};
watcher.Start();
// When monitoring ends, do not forget watcher.Stop() and Dispose
7.5. For Always-On Monitoring, Design the Re-Registration After a Subscription Drops
When you build this into always-on monitoring, include re-registration after the subscription drops (on service restart, or after an error) in the design. The design thinking behind “checking and displaying state”, including device monitoring, is covered in “Best Practices for Checking and Displaying External Device State”.
stateDiagram-v2
accTitle: The Subscription Lifecycle in Always-On Monitoring
accDescr: In always-on monitoring the subscribed state can drop on a service restart or an error, so the design has to include detecting that it dropped, re-registering, and returning to the subscribed state
s1: Subscribed
s2: Subscription dropped
s3: Re-register
[*] --> s1
s1 --> s2: Service restart or error
s2 --> s3
s3 --> s1
Figure 16: Always-on monitoring has to include the design for re-registering and returning to the subscribed state when a subscription drops.
8. Isolating Failures: Performance, Bitness, and the Repository
If you cannot connect, go back to Section 5.2; if only the subscription is denied, Section 7.1; for casting and date handling, Sections 6.2 and 6.4. This section separates the other things you run into often: “slow”, “wrong value”, and “class not found”.
8.1. Slow: Review How Much You Retrieve, How Often, and Your Sessions
A WMI query is a piece of work in which “the provider produces the values on the spot”, and it is not free. There are two classic anti-patterns.
- Reaching for
SELECT *out of habit. Retrieving every instance ofWin32_Processwith every property inflates both the provider’s work and, for remote queries, the network transfer. Narrow rows with-Filterand columns with-Property, and use-KeyOnlyif all you want is the keys for a follow-up operation. All of these are officially provided means of “reducing the size of the objects and the network traffic”.3 - Short-interval polling. A design like “
Get-CimInstance Win32_Processonce per second” should be replaced by the event subscription from Section 7. Even when you really do have to use the polling form (WITHIN), widen the interval to the smallest one that still meets the requirement.
Repeating -ComputerName one machine at a time against remote targets is also quite wasteful. A temporary session is created on every query, so change multiple operations over to reusing a CIM session.3
flowchart TB
accTitle: Performance Anti-Patterns and What to Replace Them With
accDescr: Reaching for SELECT asterisk out of habit is replaced by narrowing rows and columns with Filter and Property and using KeyOnly when only the keys are needed, short-interval polling is replaced by an event subscription, and repeating ComputerName one machine at a time is replaced by reusing a CIM session
a1["Reaching for SELECT * out of habit"] --> f1["Narrow with Filter and Property"]
f1 -.-> f2["KeyOnly if you only want the keys"]
a2["Short-interval polling"] --> f3["Replace with an event subscription"]
a3["ComputerName one machine at a time"] --> f4["Reuse a CIM session"]
Figure 17: Narrowing rows, columns, and keys, the right monitoring method, and session reuse keep unnecessary load down.
8.2. Wrong Value: Check the 32-bit and 64-bit Providers
On 64-bit Windows, some providers exist in both 32-bit and 64-bit versions, and by default the one that matches the bitness of the calling application responds.12 The classic case is the registry provider (StdRegProv) in root\default: read it from a 32-bit application and you get the values on the Wow6432Node side (the 32-bit view).12 When “the registry value I read through WMI is different from what regedit shows”, suspect this first.
If you need the other view, you can request it explicitly by specifying __ProviderArchitecture in the context at connection time (plus __RequiredArchitecture if you want to make it mandatory).12 The full picture of the bitness problem is also covered in “Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide”.
flowchart TB
accTitle: Provider Selection in a 64-bit Environment
accDescr: By default the provider matching the bitness of the calling application responds, so a registry query from a 32-bit application receives the values on the Wow6432Node side, but specifying __ProviderArchitecture lets you explicitly request the other view
q1{"What is the caller's bitness?"} -->|32-bit| p32["The 32-bit provider responds"]
q1 -->|64-bit| p64["The 64-bit provider responds"]
p32 -.-> wow["The registry values come from Wow6432Node"]
ctx["Specify __ProviderArchitecture"] -.-> ov["Explicitly request the other view"]
Figure 18: By default the side matching the caller’s bitness responds, so a 32-bit application reads the Wow6432Node side.
8.3. Class Not Found: Verify the Repository Before You Repair It
WMI class definitions are stored in the repository (not a single file: the set of files inside the Repository folder functions as the database13). When it becomes inconsistent, errors such as “a class that should exist cannot be found” or “invalid namespace” start appearing even though nothing changed on the application side.
Use winmgmt.exe to isolate and repair it.13
rem Consistency check (a result of inconsistent means there is an inconsistency)
winmgmt /verifyrepository
rem Consistency check plus a rebuild if there is an inconsistency (readable content is merged)
winmgmt /salvagerepository
What you have to watch out for is not making deletion or reinitialization of the repository your first move. Errors that surface through WMI can originate elsewhere in the OS, and Microsoft states explicitly that making repository deletion the first remedy “may cause damage to the system or to installed applications”.13 Keep the order: verify with /verifyrepository, then repair with /salvagerepository.
flowchart TB
accTitle: Isolating a WMI Repository Inconsistency
accDescr: When errors such as a class not being found appear, check consistency with winmgmt verifyrepository, rebuild with salvagerepository if it is inconsistent, and never make deletion or reinitialization of the repository your first move
sym["Errors such as class not found"] --> verify["winmgmt /verifyrepository"]
verify --> q1{"Is the result inconsistent?"}
q1 -->|Yes| salvage["winmgmt /salvagerepository"]
q1 -->|No| other["Suspect a cause elsewhere in the OS"]
salvage -.-> merge["Readable content is merged"]
del["Deleting or reinitializing the repository"] -.-> ng["Not your first move"]
Figure 19: Keep the order of verifying first and then salvaging, and do not make deletion your first move.
9. Summary: Connecting Queries to Monitoring and Operations
WMI/CIM is a common entry point for querying Windows management information across the board. It is not a tool for funneling everything into WMI, including work a dedicated API already covers.
| Stage of building it in | What to check |
|---|---|
| Choosing the tool | Prefer a purpose-built mechanism for settings, high-frequency performance monitoring, and one-off OS operations, and use WMI/CIM for information retrieval and remote queries |
| Trying it in PowerShell | Write with the CIM cmdlets even for 5.1. Pick a class in root/CIMV2 and narrow rows, columns, and keys |
| Extending it to remote machines | Get the WSMan/WinRM connection requirements in place, and reuse a session for multiple operations. Consider the DCOM option for targets that need it |
| Moving it into C# | Choose between System.Management, which suits local queries, and the MI API, which suits remote queries and monitoring. Check how types and dates are handled |
| Turning it into always-on monitoring | Check the privileges Win32_ProcessStartTrace requires, and design subscribing, unsubscribing, and re-registering after a disconnect as one continuous flow |
| Investigating a failure | Isolate short-interval polling, the provider’s bitness, and repository consistency. Do not make deletion your first remedy |
Once you think of the CIM standard and the WMI implementation, queries and event subscriptions, and connections and access permissions as separate things, it becomes clear which tool to choose and what to check. Start by retrieving the information you need in PowerShell, then carry that result through to your C# implementation and operational design.
Related Articles
- How to Run PowerShell from C# (CSharp) and Receive the Results as Objects
- Practical PowerShell Command Recipes — Growing the Small Tools You Use Every Day
- The Differences Between Windows PowerShell 5.1 and PowerShell 7 — A Practical Guide to Migrating In-House Scripts
- Best Practices for Checking and Displaying External Device State - Designing Beyond a Single “Connected”
- Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)
- What Is the TPM in Windows? — An Illustrated Guide to the “Safe That Never Lets Keys Out” and Measured Boot
Related Consulting Areas
At Komura Software LLC, we handle building WMI/CIM-based hardware information retrieval, process monitoring, and remote PC queries into business applications, migrating in-house scripts built on Get-WmiObject to the CIM cmdlets, and investigating the class of problem where “it works on the development machine but hits a permissions error at the customer site”. You can bring us in for everything from prototyping in PowerShell through the real implementation in C#, as one continuous engagement.
- Windows Application Development
- Bug Investigation and Root Cause Analysis
- Technical Consulting and Design Review
- Contact
References
-
Microsoft Learn, About WMI. On WMI being Microsoft’s implementation of WBEM (an industry initiative that develops standard technologies for accessing management information in enterprise environments), on its use of the CIM (Common Information Model) industry standard to represent management targets and on CIM being developed and maintained by the DMTF (Distributed Management Task Force), on the next-generation MI (Windows Management Infrastructure) being fully compatible with classic WMI, and on remote WMI connections being made over DCOM with WS-Management-based WinRM as the alternative. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. On the WMI v1 cmdlets (Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject) having been removed from PowerShell, and on the cmdlets in the CimCmdlets module (WMI v2) providing the same functionality with new features and a redesigned syntax. ↩ ↩2 ↩3
-
Microsoft Learn, Get-CimInstance (CimCmdlets). On connecting to local WMI with a COM session when neither ComputerName nor CimSession is specified and creating a temporary session over the WsMan protocol when -ComputerName is specified, on a CIM session connection being recommended for performance when performing multiple operations against the same computer, on -Filter being a WQL/CQL where clause that does not include the WHERE keyword, on -Property and -KeyOnly reducing object size and network traffic, on the default namespace being root/CIMV2 and the default query dialect (-QueryDialect) being WQL, on the output being Microsoft.Management.Infrastructure.CimInstance, on the GetOwner call example combined with Invoke-CimMethod, and on the cmdlet being Windows-only. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). On CIM session options having two parameter sets, one for WsMan and one for DCOM, on -Protocol accepting Dcom / Default / Wsman, on the example that passes an option created with New-CimSessionOption -Protocol Dcom to the -SessionOption parameter of New-CimSession to create a DCOM CIM session, and on the default impersonation level of a DCOM session being Impersonate. ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). On this being the most common entry class for retrieving management information, which retrieves a collection of management objects based on a specified WQL query, on it taking an ObjectQuery and a ManagementScope (a WMI namespace) and returning a ManagementObjectCollection from Get(), and on System.Management.dll being provided as the NuGet package System.Management. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CimSession Class (Microsoft.Management.Infrastructure). On Microsoft.Management.Infrastructure.dll being provided as the NuGet package Microsoft.Management.Infrastructure, on creating a session with Create(computerName), on running a query with QueryInstances(namespace, queryDialect, query), and on the class providing EnumerateInstances / GetInstance / InvokeMethod / Subscribe together with the asynchronous variant of each (*Async) and implementing IDisposable. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). On subscribing to indications (events) by class name or query expression and naming the subscription with -SourceIdentifier, on the Win32_ProcessStartTrace subscription example and the note that running it requires PowerShell to be run as administrator, on the example that references ProcessName / ProcessId from $Event.SourceEventArgs.NewEvent inside the -Action script block, on creating a temporary WsMan session when -ComputerName is specified and connecting locally over COM when it is not, and on using Unregister-Event to remove a subscription. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Win32_ProcessStartTrace class. On this being an event class that indicates the start of a new process and having properties such as ProcessName / ProcessID / ParentProcessID / SessionID / Sid, on the SECURITY_DESCRIPTOR property being the descriptor by which the event provider determines which users can receive the event, and on the class living in the Root\CIMV2 namespace and being supplied by the kernel trace provider (Krnlprov.dll). ↩ ↩2 ↩3
-
Microsoft Learn, Win32_LogicalDisk class. On Win32_LogicalDisk being a class derived from CIM_LogicalDisk that represents a local storage device, on the values of DriveType (2 = removable, 3 = local disk, 4 = network drive, 5 = CD, and so on), on FreeSpace / Size being uint64 byte values, on DeviceID being the key, and on the VBScript and C# query examples that filter with DriveType = 3. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Installation and configuration for Windows Remote Management. On no WinRM listener being configured by default so that WS-Management messages can be neither sent nor received, on winrm quickconfig setting the service to start automatically, configuring HTTP/HTTPS listeners, and registering a firewall exception, on the default ports for WinRM 2.0 being HTTP 5985 / HTTPS 5986, on setting TrustedHosts as narrowly as possible when mutual authentication (Kerberos) cannot be established, as in a workgroup, and on the default security descriptor (RootSDDL) that controls remote access to the listener and the additional configuration required to let non-administrator users use the WMI plug-in. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System.Management Namespace. On this being the namespace that queries the WMI infrastructure through the ManagementObjectSearcher family of classes and subscribes to events with ManagementEventWatcher, on WqlEventQuery representing an event query in WQL form, and on ManagementDateTimeConverter providing methods to convert between DMTF datetimes and intervals and the CLR’s DateTime / TimeSpan. ↩ ↩2 ↩3
-
Microsoft Learn, Requesting WMI Data on a 64-bit Platform. On the 32-bit provider responding to 32-bit applications (including scripts) and the 64-bit provider responding to 64-bit applications by default where both a 32-bit and a 64-bit version of a provider exist, on the context values __ProviderArchitecture (32 or 64) and __RequiredArchitecture letting you request and enforce the non-default provider (with WBEM_E_PROVIDER_LOAD_FAILURE if the requested version does not exist when enforced), and on the registry provider example in which a 32-bit client receives data from the HKLM\SOFTWARE\Wow6432Node side. ↩ ↩2 ↩3
-
Microsoft Learn, winmgmt. On winmgmt.exe’s /verifyrepository performing a consistency check of the WMI repository, on /salvagerepository performing a consistency check and rebuilding the repository when an inconsistency is detected while merging the content it could read, on /resetrepository returning it to the state it was in at initial OS installation, on the repository being a set of files inside the Repository folder that functions as a database, and on errors surfacing through WMI sometimes originating elsewhere in the OS, so that deleting the repository as a first remedy should not be done because it may cause damage to the system or to installed applications. ↩ ↩2 ↩3
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Windows App Outsourcing and Custom Software Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or custom software development, here is how to sort out existing software modification, devi...
Why Arguments Break — The Rules of Windows Command-Line Arguments
Windows passes CreateProcess a single string that the receiver splits. Covers the CommandLineToArgvW, CRT, and .NET rules, ArgumentList, ...
End of Servicing for Windows Printer Drivers — How Business Apps Should Prepare Their Report and Label Printing
Microsoft is phasing out v3/v4 printer drivers. What Windows protected print mode removes, and how to inventory and prepare report and la...
Practical Multithreading Best Practices: .NET Edition — What to Decide Before You Add More Threads
Keep .NET/C# threads from crashing or hanging. Ride on Task, cut shared mutable state, lock with discipline, stop with CancellationToken,...
Japanese Era Dates, Public Holidays, and Closing-Date Processing in Business Apps — Era-Resilient Design, JapaneseCalendar, and Business-Day Calculations in Practice
A report that must show '令和8年' in the Japanese era, business-day calculations that exclude public holidays, payment due on the last busin...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Frequently Asked Questions
Common questions about the topic of this article.
- What is the difference between WMI and CIM?
- CIM is the industry-standard model for representing management targets such as systems and devices, defined and maintained by the DMTF (Distributed Management Task Force). WMI is Microsoft's implementation of WBEM, an initiative that uses that standard, and it is built into Windows. In other words, CIM is the specification and WMI is the implementation on Windows. PowerShell's Get-CimInstance and C#'s Microsoft.Management.Infrastructure call themselves "CIM" because they are APIs that follow this standard, but they connect to the same underlying WMI. For day-to-day development, it is enough to understand it as querying WMI classes (Win32_* and so on) through CIM-family APIs.
- Can I no longer use Get-WmiObject?
- It still works in Windows PowerShell 5.1, but from PowerShell 6 onward (the current PowerShell 7), the WMI v1 cmdlets Get-WmiObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance, and Remove-WmiObject have been removed and cannot be run. The same functionality is provided by the CimCmdlets module (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent, and so on). When writing new scripts, it is safer to write them with the CIM cmdlets even if they will run under 5.1. Doing so means you never have to rewrite the WMI portion when you migrate to PowerShell 7.
- Should I use System.Management or Microsoft.Management.Infrastructure to access WMI from C#?
- Both are Windows-only, and from current .NET you bring them in as NuGet packages. System.Management is the classic API, usable just by passing WQL to a ManagementObjectSearcher, and it is enough on its own if you are mainly retrieving local information. It also includes ManagementDateTimeConverter, which converts DMTF dates. Microsoft.Management.Infrastructure (the MI API), on the other hand, shares the same type system (CimSession / CimInstance) as PowerShell's CIM cmdlets, and it handles remote queries over WSMan, asynchronous method variants, and event subscription (Subscribe) in one consistent way. If you are building remote PC queries and monitoring into a real product, choosing the MI API is the sensible call.
- Get-CimInstance will not connect to a remote PC. What should I check?
- First check whether WinRM is configured on the target machine. A CIM operation that specifies -ComputerName creates a temporary session over the WSMan (WinRM) protocol, so it assumes the WinRM service and a listener are running on the target. winrm quickconfig performs the default configuration: starting the service, creating a listener, and adding a firewall exception. The default ports are 5985 for HTTP and 5986 for HTTPS, so also check any firewalls along the path. In a workgroup environment, mutual authentication through Kerberos is not available, so you may need to register the target in the client's TrustedHosts list. For a target where you absolutely cannot configure WinRM, you can instead connect over DCOM using an option created with New-CimSessionOption -Protocol Dcom.
- Why does a WMI date come back in a format like "20260801100000.000000+540"?
- WMI dates are stored in the string format defined by the DMTF CIM specification (yyyymmddHHMMSS.mmmmmm±UUU, where the trailing value is the offset from UTC in minutes). If you read the raw value with the old Get-WmiObject or with System.Management, you get this string as-is. In C# (System.Management), ManagementDateTimeConverter provides methods to convert between the DMTF format and DateTime / TimeSpan, so use those rather than slicing the string yourself. Note that when you retrieve data through a CIM-family API such as Get-CimInstance, date properties are already returned converted to DateTime, so you never run into this problem at all.