WMI/CIM из C# и PowerShell — практическое руководство по сведениям об оборудовании, мониторингу процессов и удалённым запросам

· · Windows, C#, .NET, PowerShell, WMI, CIM, Бизнес-приложения, Разработка для Windows

«Хочу показать серийный номер и модель ПК на экране бизнес-приложения.» «Хочу следить за свободным местом на диске сервера и поднимать предупреждение.» «Хочу обнаруживать, что запустился конкретный процесс.» «Хочу собрать состояние ПК в другом месте в одном запросе.» — такие требования в разработке бизнес-приложений и средств администрирования для Windows встречаются постоянно. И стандартный ответ на них — WMI (Windows Management Instrumentation), или, стандартным именем, CIM (Common Information Model).

Типичные требования и WMI/CIMПоказать серийный номер и модель, следить за свободным местом на диске, обнаруживать запуск процессов и запрашивать удалённые ПК — типичные требования бизнес-приложений, стандартный ответ на которые есть WMI, а стандартное имя — CIMСерийный номер и модельWMI(стандартное имя — CIM)Мониторинг свободного места на дискеОбнаружение запуска процессовЗапрос к удалённым ПК

Рис. 1: На четыре типичных требования бизнес-приложений стандартный ответ — WMI/CIM.

Сложность в том, что сведения о WMI смешаны из старого и нового. В поиске рядом лежат статьи десятилетней давности с Get-WmiObject и статьи с Get-CimInstance, а на стороне C# есть две линии: System.Management и Microsoft.Management.Infrastructure. Трудно понять, что является актуальным способом писать, а что — «ещё работает, но для нового кода не выбирать». На деле Get-WmiObject в PowerShell 7 нет вовсе, и это внезапно всплывает при миграции внутренних скриптов, написанных под 5.1.

Статья рассчитана на разработчиков C#/PowerShell, которые встраивают в бизнес-приложения получение сведений об оборудовании, мониторинг процессов и запросы к удалённым ПК. Она собирает — опираясь на первичные источники по состоянию на август 2026 — минимум структуры WMI/CIM, командлеты CIM в PowerShell, два API в C#, часто используемые рецепты, ловушки производительности, прав и 64-бит, и наконец критерий «когда WMI использовать не стоит».

1. Сначала вывод

  • CIM — отраслевой стандарт сведений управления от DMTF, WMI — его реализация Microsoft. API семейства «CIM» в PowerShell и C# — актуальное поколение API, следующее этому стандарту, и подключаются они к той же инфраструктуре WMI.1
  • В PowerShell актуально поколение командлетов CIM (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent). Старые командлеты WMI (Get-WmiObject и ещё четыре) удалены начиная с PowerShell 6 и в PowerShell 7 не работают.2
  • Пространство имён по умолчанию — root/CIMV2, и повседневные запросы в основе — сужение классов Win32_* там через WQL.3
  • Удалённые запросы по умолчанию идут по WSMan (WinRM). Указание -ComputerName создаёт временную сессию WSMan. Если к одному и тому же узлу обращаться несколько раз, проверенный приём по производительности — повторно использовать сессию CIM (New-CimSession); для старых целей, где WinRM настроить нельзя, есть параметр протокола DCOM.34
  • В C# две линии: System.Management (ManagementObjectSearcher) и Microsoft.Management.Infrastructure (CimSession). Обе только для Windows; в актуальном .NET их ставят из NuGet. Если удалённые запросы или мониторинг встраиваются всерьёз, лучше API MI — та же система типов, что у командлетов CIM.56
  • Запуск процессов ловите подпиской на события, а не опросом (polling). Подписка на Win32_ProcessStartTrace должна выполняться с правами администратора.78
  • Не используйте SELECT * по привычке. Сужать передаваемые данные через -Filter / -Property / -KeyOnly — способ предотвратить примерно половину проблем производительности WMI.3
  • WMI — не универсальное решение. Для высокочастотного мониторинга производительности, чтения и записи настроек своего приложения, разовых вызовов функций ОС лучше подойдут счётчики производительности, реестр, Win32 API или специализированные командлеты (таблица выбора в главе 8).

2. Что такое WMI/CIM — стандарт и реализация, пространства имён, классы, WQL

Сначала один раз разложим отношение терминов.

Термин Что это
CIM (Common Information Model) Отраслевая стандартная модель представления объектов управления: систем, приложений, сетей, устройств. Разрабатывает и поддерживает DMTF (Distributed Management Task Force)1
WBEM (Web-Based Enterprise Management) Отраслевая инициатива по созданию стандартных технологий доступа к сведениям управления в корпоративной среде1
WMI Реализация Microsoft WBEM. Представляет объекты управления по стандарту CIM и встроена в Windows1
MI (Windows Management Infrastructure) Следующее поколение WMI. Полностью совместимо с прежним WMI; большинство новых поставщиков пишут на MI1
Связь стандарта CIM и реализации WMIСтандарт CIM, который разрабатывает и поддерживает DMTF, используют в рамках инициативы WBEM, реализация Microsoft — WMI, следующее поколение MI полностью совместимо с прежним WMI, а API семейства CIM подключаются к той же инфраструктуре WMIРазрабатывает и поддерживает DMTFCIM(отраслевая стандартная модель)WBEM(отраслевая инициатива)WMI(реализация Microsoft)MI(следующее поколение, полная совместимость)API семейства CIM(PowerShell / C#)

Рис. 2: CIM — спецификация, WMI — реализация на Windows. API семейства CIM подключаются к той же инфраструктуре WMI.

Как разработчику стоит держать в голове четыре структурных куска.

  • Пространство имён (namespace): иерархия, которая группирует классы. В повседневных запросах почти всегда используется root/CIMV2, это же значение по умолчанию у командлетов CIM.3 Есть и другие, например root\default (поставщик реестра и прочее).
  • Класс: тип объекта управления, например Win32_ComputerSystem (сам компьютер), Win32_LogicalDisk (логический диск), Win32_Process (процесс). Классы, специфичные для Windows и унаследованные от классов стандарта CIM (например CIM_LogicalDisk), несут префикс Win32_.9
  • Поставщик: компонент, который даёт содержимое класса. При запросе поставщик на месте спрашивает ОС и собирает значения.
  • WQL: язык запросов, похожий на SQL. Как в SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto', класс трактуется как таблица и сужается. Язык запросов по умолчанию у командлетов CIM тоже WQL.3

«Читать сведения об ОС и оборудовании через единый набор классов и язык запросов» — в этом ценность WMI. Обратная сторона: запись и операции ограничены теми классами, у которых есть методы, вызываемые через Invoke-CimMethod; это не механизм, который умеет всё.

Структура запроса WMIЗапрос WQL направлен на классы Win32_* внутри пространства имён root/CIMV2, поставщик, который даёт содержимое класса, на месте спрашивает ОС и собирает значения, после чего возвращается результатЗапрос на WQLПространство имён root/CIMV2Класс Win32_*ПоставщикНа месте спрашивает ОСВозвращает результат

Рис. 3: Запрос идёт по пространству имён, классу и поставщику, а значения собираются на месте.

3. Использование из PowerShell — актуальны командлеты CIM, командлеты WMI удалены

3.1. Основа — Get-CimInstance

# По имени класса (пространство имён по умолчанию root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem

# В -Filter пишем только содержимое WHERE (само ключевое слово WHERE не пишем)
Get-CimInstance -ClassName Win32_Service -Filter "StartMode = 'Auto' AND State <> 'Running'"

# Берём только нужные свойства, чтобы уменьшить объём передачи
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, CreationDate

# Если хотим писать WQL как есть — -Query
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name LIKE 'p%'"

-Filter — это ровно предложение WHERE языка WQL, -Property ограничивает получаемые столбцы.3 Возвращаемое значение — объект CimInstance, и свойства дат (CreationDate, LastBootUpTime и подобные) уже преобразованы в DateTime. В отличие от старого Get-WmiObject, полученный объект не несёт методы напрямую, поэтому вызов метода передают в Invoke-CimMethod.

Вызов методов CimInstanceCimInstance, который возвращает Get-CimInstance, отдаёт свойства дат уже преобразованными в DateTime, но методов напрямую не имеет, поэтому вызов метода делают, передавая экземпляр в Invoke-CimMethodGet-CimInstanceОбъект CimInstanceДаты уже преобразованы в DateTimeМетодов напрямую нетПередать в Invoke-CimMethodВызов метода

Рис. 4: У CimInstance нет методов, поэтому вызов метода делают, передавая экземпляр в Invoke-CimMethod.

# Вызов метода экземпляра: получить владельца каждого процесса
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
    Invoke-CimMethod -MethodName GetOwner

# Вызов статического метода класса: запустить процесс
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }

# Посмотреть определение класса (список свойств и методов)
Get-CimClass -ClassName Win32_Process

3.2. Таблица соответствия при переходе со старых командлетов WMI

Начиная с PowerShell 6 (включая актуальный PowerShell 7) следующие командлеты WMI v1 удалены. Ту же функциональность даёт модуль CimCmdlets (WMI v2).2

Старое (до Windows PowerShell 5.1) Актуальное (командлеты CIM) Примечание
Get-WmiObject Get-CimInstance Идея -Filter / -Query та же
Get-WmiObject -List Get-CimClass Поиск классов и проверка определения
Invoke-WmiMethod Invoke-CimMethod Аргументы передают хеш-таблицей -Arguments @{ }
Register-WmiEvent Register-CimIndicationEvent Подписка на события (раздел 6.3)
Set-WmiInstance Set-CimInstance Изменение записываемых свойств
Remove-WmiObject Remove-CimInstance Удаление экземпляра

Командлеты CIM работают и в Windows PowerShell 5.1, поэтому всё новое стоит писать на стороне CIM даже если оно будет крутиться под 5.1 — так не остаётся стоимости миграции. Общая картина сосуществования и перехода 5.1 и 7 собрана в «Различия между Windows PowerShell 5.1 и PowerShell 7».

Зачем писать новые скрипты на CIMСкрипт на командлетах WMI в 5.1 работает, но начиная с PowerShell 6 они удалены, поэтому при миграции нужна переписка, а командлеты CIM работают и в 5.1, так что если новое писать на CIM, стоимости миграции не остаётсяКомандлеты WMIКомандлеты CIMНовый скриптНа чём писать?В 5.1 работаетРаботает и в 5.1В PowerShell 7 удаленыПереписка при миграцииСтоимости миграции не остаётся

Рис. 5: Если новое писать командлетами CIM, при переходе на PowerShell 7 переписывать не придётся.

4. Удалённые запросы — сессии CIM (WSMan по умолчанию) и параметр DCOM

Командлеты CIM без указания цели подключаются к локальному WMI по COM; если указать -ComputerName, они подключаются, создавая временную сессию по протоколу WSMan (WinRM). Когда против одного компьютера делают несколько операций, по производительности выгоднее создать сессию CIM и использовать её повторно.3

Выбор способа подключения CIMБез указания — подключение COM к локальному WMI, при ComputerName каждый запрос создаёт временную сессию WSMan, несколько операций к одному узлу выгоднее гонять через повторное использование New-CimSession, а для цели без настроенного WinRM есть параметр протокола DCOMнетдаразоваянесколькоВыполнение командлета CIMУказан ComputerName?Подключение COM к локальному WMIНесколько операций к одному узлу?Временная сессия WSManПовторно использовать New-CimSessionСоздаётся на каждый запросЦель без настроенного WinRMПараметр протокола DCOM

Рис. 6: Удалённые запросы по умолчанию идут по WSMan; несколько операций к одному узлу принято гонять через повторное использование сессии CIM.

# Для разового запроса — -ComputerName (каждый раз создаётся временная сессия)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02

# Для повторных запросов — повторно использовать сессию CIM
$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

Для целей, до которых нельзя дойти по WSMan — например старых машин, где WinRM не настроить, — можно выбрать протокол DCOM.4

$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom

Предпосылки удалённого запроса таковы.

  • На цели должен быть настроен WinRM. winrm quickconfig за один проход ставит службе автозапуск, создаёт прослушиватель HTTP (порт по умолчанию 5985) и регистрирует исключение брандмауэра.10 Если нужно подключаться по HTTPS (порт по умолчанию 5986), этого мало: готовят сертификат сервера и отдельно настраивают прослушиватель HTTPS, например через winrm quickconfig -transport:https.10
  • На брандмауэрах пути должны быть открыты соответствующие порты. Практику проектирования и регистрации входящих правил разбирает статья «Брандмауэр Windows и бизнес-приложения».
  • Проверка подлинности. В домене взаимную проверку даёт Kerberos. В рабочей группе Kerberos недоступен, поэтому может понадобиться регистрация цели в TrustedHosts на клиенте. Список держите как можно уже.10
  • Права. В конфигурации по умолчанию удалённые запросы и операции WMI в основе выполняют учётной записью из группы администраторов цели. Чтобы открыть это обычным пользователям, нужно настроить разрешения и в WinRM, и в пространстве имён WMI.10
  • Заметьте, что у DCOM нет фиксированного порта прослушивания (используются динамические порты RPC), поэтому проектировать переход через брандмауэр сложнее. Для того, что вы строите дальше, безопаснее считать WSMan значением по умолчанию.
Проверка предпосылок удалённого запросаНа цели winrm quickconfig за один проход ставит службе автозапуск, создаёт прослушиватель HTTP и регистрирует исключение брандмауэра, прослушиватель HTTPS настраивают отдельно с сертификатом, а в рабочей группе может понадобиться регистрация в TrustedHostswinrm quickconfigАвтозапуск службыСоздание прослушивателя HTTP(5985)Исключение брандмауэраПрослушиватель HTTPS(5986)Готовят сертификат и настраивают отдельноСреда рабочей группыРегистрация в TrustedHosts

Рис. 7: winrm quickconfig за один проход делает конфигурацию по умолчанию; прослушиватель HTTPS и проверку подлинности в рабочей группе закрывают отдельно.

5. Использование из C# — System.Management и Microsoft.Management.Infrastructure

Для обращения к WMI из C# есть две линии API. Обе только для Windows.

  System.Management Microsoft.Management.Infrastructure (API MI)
Подключение В .NET Framework входит по умолчанию. В актуальном .NET пакет NuGet System.Management5 Пакет NuGet Microsoft.Management.Infrastructure6
Класс входа ManagementObjectSearcher (запрос передачей WQL)5 CimSession (Create → QueryInstances / InvokeMethod / Subscribe)6
Система типов ManagementObject / ManagementEventWatcher11 CimInstance / CimSessionте же типы, что у командлетов CIM3
Удалённый доступ На базе DCOM На базе WSMan (сессия CIM). Есть асинхронные варианты (*Async)6
Когда уместно Получение локальных сведений. Сопровождение существующего кода Встраивание удалённых запросов и мониторинга. Проектирование в паре с PowerShell

5.1. System.Management: основы ManagementObjectSearcher

WQL передают строкой и получают коллекцию результатов через Get().5

// NuGet: System.Management (только Windows)
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"]} свободно {freeGb:F1} GB / всего {sizeGb:F1} GB");
}

Свойства возвращаются как object через индексатор, поэтому нужно свериться с документацией класса по типу CIM (в этом примере FreeSpace / Size — uint649) и привести тип. Классический первый камень преткновения — решить, что это int, привести, и получить InvalidCastException.

Ловушка получения свойства и приведения типаВ System.Management свойства возвращаются как object через индексатор, поэтому нужно свериться с типом CIM в документации класса и привести, а если решить что это int и привести, получится InvalidCastExceptionПолучение через индексаторВозвращается как objectСверка типа CIM в документацииПриведение к верному типуРешили что int и привелиInvalidCastException

Рис. 8: Свойства возвращаются как object, поэтому тип CIM сверяют до приведения.

5.2. API MI: основы CimSession

CimSession обрабатывает локальный и удалённый доступ в одной форме. Перечисление, запрос, вызов методов, подписка на события и асинхронные варианты собраны в одном месте.6

// NuGet: Microsoft.Management.Infrastructure (только Windows)
using Microsoft.Management.Infrastructure;

// Локально — CimSession.Create(null), удалённо — передать имя компьютера
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 / 1024.0 / 1024 / 1024:F1} GB");
}

Поскольку обрабатывается тот же CimInstance, что возвращают командлеты CIM в PowerShell, поток разработки «сначала попробовать в PowerShell, потом перенести в C#» стыкуется естественно. Если проектируете саму связку C# и PowerShell, см. также «Как запустить PowerShell из C# (CSharp) и получить результат в виде объектов».

Поток от пробы в PowerShell к переносу в C#Командлеты CIM в PowerShell и API MI в C# работают с одним типом CimInstance, поэтому поток разработки — прототип в PowerShell, затем перенос в C# — стыкуется естественноПрототип в PowerShellКомандлеты CIMОсновная реализация на C#API MIОдин тип CimInstanceПеренос стыкуется естественно

Рис. 9: Командлеты CIM и API MI работают с одним типом CimInstance, поэтому прототип напрямую переходит в основную реализацию.

6. Часто используемые рецепты

6.1. Краткая таблица обычных классов

Нужные сведения Класс Основные свойства
Производитель / модель Win32_ComputerSystem Manufacturer, Model
Серийный номер корпуса Win32_BIOS SerialNumber
Версия ОС / время загрузки Win32_OperatingSystem Caption, Version, LastBootUpTime
Свободное место на диске Win32_LogicalDisk DeviceID, FreeSpace, Size, DriveType9
Состояние службы Win32_Service Name, State, StartMode
Список процессов Win32_Process Name, ProcessId, CommandLine

6.2. Классика учёта активов: серийный номер, модель и свободное место на диске

# Сведения о модели и серийный номер (для сверки с реестром активов ПК)
$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
}

# Свободное место на локальных дисках (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 — значение «локальный диск», оно исключает съёмные (2), сетевые (4) и CD (5).9 Для мониторинга достаточно прогнать этот скрипт по каждому серверу через сессию CIM — и получится основа дискового мониторинга без агента.

Основа дискового мониторинга без агентаФильтр DriveType 3 исключает съёмные, сетевые и CD и оставляет только локальные диски, тот же скрипт гоняют по серверам через сессию CIM и получают основу дискового мониторинга без агентаСкрипт свободного местаСузить DriveType = 3Исключить съёмные и прочееЧерез сессию CIMПрогнать по серверамМониторинг без агента

Рис. 10: Основа мониторинга — прогнать скрипт, суженный до локальных дисков, по серверам через сессию CIM.

6.3. Обнаружение запуска процессов — подписка на события

Вместо «периодически опрашивать Win32_Process и смотреть разницу» используйте подписку на события. Проще всего ловить запуск процесса подпиской на Win32_ProcessStartTrace (класс события поставщика трассировки ядра, со свойствами вроде ProcessName / ProcessID / ParentProcessID8).

# Выполнять в сессии PowerShell с правами администратора
$action = {
    $name = $Event.SourceEventArgs.NewEvent.ProcessName
    $id   = $Event.SourceEventArgs.NewEvent.ProcessID
    Write-Host "Процесс запущен: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
    -SourceIdentifier ProcessStarted -Action $action

Подписка живёт, пока жива сессия PowerShell, в которой её зарегистрировали, и -Action выполняется при каждом запуске процесса. Не выполняйте команду снятия следом в том же пакете — подписка просто исчезнет до начала мониторинга. Снятие выполняют когда мониторинг закончен.

# Когда мониторинг закончен: снять подписку
Unregister-Event -SourceIdentifier ProcessStarted

Register-CimIndicationEvent регистрирует подписку по имени класса или по событийному запросу WQL, и скрипт-блок -Action выполняется при каждом приходе события.7 Подписка на этот класс требует прав администратора.7 Кто может получать событие, контролирует дескриптор безопасности класса события; обычный пользователь без повышения прав получит отказ в доступе.8

Поток подписки на событие запуска процессаВ сессии PowerShell с правами администратора Register-CimIndicationEvent регистрирует подписку, при каждом запуске процесса приходит событие и выполняется Action, а по окончании мониторинга снимают через Unregister-EventWMIСессия PowerShellWMIСессия PowerShellЗапуск с правами администратораРегистрация подписки через Register-CimIndicationEventСобытие при каждом запуске процессаВыполнение -ActionСнятие через Unregister-Event(по окончании мониторинга)

Рис. 11: Подписка действует, пока жива зарегистрировавшая её сессия; снятие делают, когда мониторинг закончен.

Другой способ — универсальное событие создания экземпляра (__InstanceCreationEvent), применимое к любому классу. Здесь WMI опрашивает с интервалом, заданным WITHIN, и превращает разницу в события, поэтому компромисс между интервалом обнаружения и нагрузкой выбираете вы сами.

Два способа подписки для обнаружения запуска процессовWin32_ProcessStartTrace — способ подписаться на класс события поставщика трассировки ядра, универсальный __InstanceCreationEvent опрашивает с интервалом WITHIN и превращает разницу в события, поэтому компромисс между интервалом обнаружения и нагрузкой выбираете самиОбнаружение запуска процессовWin32_ProcessStartTrace__InstanceCreationEventПодписка на трассировку ядраОпрос с интервалом WITHINКомпромисс интервала и нагрузки

Рис. 12: Подписаться на выделенный класс события или использовать универсальное событие создания экземпляра с интервалом опроса.

# Следить за новыми экземплярами Win32_Process опросом каждые 5 секунд
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"
Register-CimIndicationEvent -Query $query -SourceIdentifier ProcPoll -Action {
    Write-Host "Запущен: $($Event.SourceEventArgs.NewEvent.TargetInstance.Name)"
}

В C# (System.Management) ту же роль играет ManagementEventWatcher.11

using System.Management;

// В процессе, запущенном от администратора
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($"Процесс запущен: {name} (PID={pid})");
};
watcher.Start();
// По окончании мониторинга не забудьте watcher.Stop() и Dispose

Если встраиваете это в постоянно работающий мониторинг, заложите в проект и повторную регистрацию подписки, когда она обрывается (при перезапуске службы, при ошибке). Проектирование «проверки и отображения состояния», включая мониторинг устройств, разобрано в «Лучшие практики проверки и отображения состояния внешних устройств».

Жизненный цикл подписки в постоянном мониторингеВ постоянном мониторинге состояние активной подписки может оборваться из-за перезапуска службы или ошибки, поэтому в проект включают обнаружение обрыва, повторную регистрацию и возврат к активной подпискеПерезапуск службы / ошибкаПодписка активнаПодписка оборваласьПовторная регистрация

Рис. 13: В постоянном мониторинге проект должен включать повторную регистрацию и возврат к активной подписке, когда она обрывается.

7. Ловушки — производительность, права, 64-бит, репозиторий и даты

7.1. SELECT * и слишком частый опрос

Запрос WMI — это «поставщик собирает значения на месте», и он не бесплатен. Два классических антипаттерна.

  • Привычное использование SELECT *. Сбор всех свойств всех строк Win32_Process раздувает работу поставщика и сетевую передачу (при удалённом доступе). Сужайте строки -Filter, столбцы -Property, а если нужны только ключи для последующей операции — -KeyOnly. Всё это официальные средства «чтобы уменьшить размер объектов и сетевой трафик».3
  • Короткий цикл опроса. Конструкцию вроде «каждую секунду Get-CimInstance Win32_Process» заменяют подпиской на события из раздела 6.3. Даже если без опроса (WITHIN) не обойтись, интервал расширяют до действительно достаточного для требования.

Кроме того, повторять -ComputerName по одной машине против удалённых узлов тоже расточительно: временная сессия создаётся на каждый запрос. Несколько операций переводят на повторное использование сессии CIM.3

Антипаттерны производительности и чем их заменитьПривычное SELECT со звёздочкой сужают Filter и Property по строкам и столбцам, для одних ключей берут KeyOnly, короткий цикл опроса заменяют подпиской на события, а повтор ComputerName по одной машине — повторным использованием сессии CIMПривычное SELECT *Сузить Filter и PropertyТолько ключи — KeyOnlyКороткий цикл опросаЗаменить подпиской на событияComputerName по одной машинеПовторно использовать сессию CIM

Рис. 14: Сужение строк, столбцов и ключей плюс замена опроса подпиской на события предотвращают половину проблем производительности WMI.

7.2. Права для подписки на события

Как в разделе 6.3, подписка семейства Win32_ProcessStartTrace предполагает права администратора.7 Авария «на машине разработки (запуск от администратора) работало, а в среде обычного пользователя у заказчика мониторинг не работает» — классика рядом с диалогом уведомления брандмауэра. Если мониторинг встраивают в бизнес-приложение, которое работает от обычного пользователя, имеет смысл вынести часть мониторинга в службу Windows (например от LocalSystem) и связать её с основным приложением межпроцессным взаимодействием.

Схема мониторинга в среде обычного пользователяПодписку, которая требует прав администратора, выносят из основного приложения, работающего от обычного пользователя, изолируя мониторинг в службе Windows от LocalSystem или подобного, и связывают с основным приложением межпроцессным взаимодействиемМежпроцессное взаимодействиеСлужба Windows для мониторингаПодписка на трассировку запускаЗапуск от LocalSystem и т. п.Основное приложение(обычный пользователь)

Рис. 15: Подписку, которой нужны права администратора, выносят в службу и связывают с основным приложением межпроцессным взаимодействием.

7.3. 32-бит / 64-бит и поставщики

В 64-битном Windows у некоторых поставщиков сосуществуют 32-битная и 64-битная версии, и по умолчанию отвечает сторона, совпадающая с разрядностью вызывающего приложения.12 Классика — поставщик реестра (StdRegProv) в root\default: чтение из 32-битного приложения возвращает значения стороны Wow6432Node (32-битное представление).12 Если «значение реестра, прочитанное через WMI, не совпадает с тем, что показывает regedit», подозревайте это в первую очередь. Если нужна противоположная сторона, её можно явно запросить, задав в контексте подключения __ProviderArchitecture (и, если нужно принудительно, __RequiredArchitecture).12 Общая картина проблем разрядности также разобрана в «Безопасный вызов Win32 API из C# — практическое руководство по P/Invoke».

Выбор поставщика в 64-битной средеПо умолчанию отвечает поставщик, чья разрядность совпадает с вызывающим приложением, запрос реестра из 32-битного приложения получает значение стороны Wow6432Node, но указанием __ProviderArchitecture можно явно запросить противоположное представление32bit64bitРазрядность вызывающего?Отвечает 32-битный поставщикОтвечает 64-битный поставщикРеестр — значение стороны Wow6432NodeУказание __ProviderArchitectureЯвно запросить противоположное представление

Рис. 16: По умолчанию отвечает сторона, совпадающая с разрядностью вызывающего, поэтому 32-битное приложение читает сторону Wow6432Node.

7.4. Симптомы и лечение повреждённого репозитория WMI

Определения классов WMI хранятся в репозитории (это не один файл: набор файлов в папке Repository работает как база данных13). Когда он становится несогласованным, начинают появляться ошибки вроде «класс, который должен существовать, не найден» или «пространство имён недействительно», хотя на стороне приложения ничего не меняли. Для диагностики и восстановления используют winmgmt.exe.13

rem Проверка целостности (результат inconsistent значит есть проблема)
winmgmt /verifyrepository

rem Проверка целостности и перестройка при проблеме (читаемое содержимое сливается)
winmgmt /salvagerepository

Важно не делать удаление или сброс репозитория первым шагом. Ошибки, которые всплывают через WMI, могут происходить из другой части ОС, и Microsoft прямо пишет, что удаление репозитория как первая реакция «может привести к повреждению системы или установленных приложений».13 Соблюдайте порядок: проверка /verifyrepository, затем восстановление /salvagerepository.

Порядок разбора несогласованности репозитория WMIЕсли появляются ошибки вроде «класс не найден», проверяют целостность winmgmt verifyrepository, при несогласованности перестраивают salvagerepository, и не делают удаление или сброс репозитория первым шагомданетОшибки вроде «класс не найден»winmgmt /verifyrepositoryРезультат inconsistent?winmgmt /salvagerepositoryИскать причину в другой части ОСЧитаемое содержимое сливаетсяУдаление или сброс репозиторияНе первым шагом

Рис. 17: Соблюдайте порядок «проверить verify, затем восстановить salvage» и не делайте удаление первым шагом.

7.5. Преобразование формата даты DMTF

Даты WMI хранятся строкой в формате DMTF спецификации CIM: yyyymmddHHMMSS.mmmmmm±UUU (суффикс — смещение от UTC в минутах, например 20260801100000.000000+540). Не разрезайте сырое значение строковой обработкой — пользуйтесь API преобразования.

  • C# (System.Management): ManagementDateTimeConverter даёт взаимное преобразование формата DMTF и DateTime / TimeSpan.11
  • API семейства CIM (Get-CimInstance / API MI): свойства дат возвращаются уже преобразованными в DateTime, поэтому с этой проблемой вы вообще не столкнётесь. (Get-CimInstance Win32_OperatingSystem).LastBootUpTime можно сразу использовать как DateTime в расчётах.
Обращение с форматом даты DMTFДаты WMI хранятся строкой формата DMTF, сырое значение из System.Management преобразуют ManagementDateTimeConverter, API семейства CIM возвращают уже DateTime, поэтому строку вручную не разрезаютSystem.ManagementAPI семейства CIMСтрока формата DMTFКаким API получено?ManagementDateTimeConverterВозвращается уже как DateTimeПреобразование в DateTime / TimeSpanРучная нарезка строкиНе использовать

Рис. 18: Преобразование строки DMTF оставляйте API преобразования; с API семейства CIM сразу используйте уже преобразованный DateTime.

8. Когда WMI использовать не стоит — таблица выбора средства

WMI силён как «единый интерфейс чтения», но не всегда оптимален. Практический ориентир выбора средства.

Что нужно сделать Подходящее средство Почему не WMI
Получение сведений об оборудовании и конфигурации ОС, удалённые запросы без агента WMI/CIM Здесь как раз сильная сторона WMI — единообразнее, чем бить по отдельным API
Чтение и запись настроек своего приложения Прямое чтение реестра (Microsoft.Win32.Registry) или файлы конфигурации Реестр через WMI — окольный путь, плюс наследуется проблема разрядности из раздела 7.3
Высокочастотный непрерывный мониторинг производительности вроде загрузки CPU Счётчики производительности (System.Diagnostics.PerformanceCounter и т. п.) Счётчики для этого и сделаны. Короткий цикл опроса WMI проигрывает и по нагрузке, и по точности
Разовый вызов функции ОС или обработка, которой нужна низкая задержка Win32 API (P/Invoke) У WMI есть накладные расходы прохода через COM/поставщика
Перечисление и операции с локальными процессами, когда прав своего процесса достаточно System.Diagnostics.Process Замыкается стандартной библиотекой, меньше зависимостей
Настройка средств администрирования Windows вроде брандмауэра или сети Специализированные командлеты на базе CIM вроде Get-NetFirewallRule Набор командлетов, собранный под задачу, точнее и безопаснее охоты по сырым классам WMI
Обнаружение изменений файлов и папок FileSystemWatcher Не несите WMI на территорию, у которой уже есть выделенный API

Ось выбора проста: «где есть выделенный механизм — им и пользоваться, а WMI/CIM оставлять сквозным и удалённым запросам». Командлеты последней строки вроде Get-NetFirewallRule внутри построены поверх CIM — форма «получить пользу WMI/CIM, не трогая его напрямую».

Ось выбора средстваОсь выбора — пользоваться выделенным механизмом там, где он есть, а WMI и CIM оставлять сквозным и удалённым запросам там, где его нет, специализированные командлеты на базе CIM — форма получить пользу, не трогая WMI и CIM напрямуюестьнетЕсть выделенный механизм?Пользоваться выделенным механизмомПользоваться WMI / CIMСквозные и удалённые запросыСпециализированные командлеты на базе CIMФорма получить только пользу

Рис. 19: Где есть выделенный механизм — им и пользоваться; сквозные и удалённые запросы закрывать WMI/CIM.

9. Итог

  • CIM — отраслевой стандарт DMTF, WMI — его реализация Microsoft. И командлеты CIM в PowerShell, и API MI в C# — актуальные точки входа, следующие этому стандарту.
  • В PowerShell актуальны Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent. Командлетов WMI вроде Get-WmiObject в PowerShell 7 нет, поэтому новые скрипты пишите на стороне CIM даже если они нацелены на 5.1.
  • Удалённые запросы по умолчанию идут по WSMan (WinRM), несколько операций повторно используют сессию CIM. Для цели без настроенного WinRM есть запасной выход — параметр DCOM.
  • В C# выбирают между System.Management (просто, ориентировано на локальное) и Microsoft.Management.Infrastructure (ориентировано на удалённое и мониторинг, та же система типов, что у командлетов CIM). Оба — пакеты NuGet только для Windows.
  • Процессы мониторьте подпиской на события, а не опросом. Подписка на Win32_ProcessStartTrace требует прав администратора.
  • Избегайте SELECT * и короткого цикла опроса — сужайте через -Filter / -Property / -KeyOnly. Помните, что запрос из 32-битного процесса обслуживает 32-битный поставщик, что даты DMTF нужно проводить через API преобразования, и что повреждённый репозиторий лечат в порядке verify → salvage, а не удалением.
  • Не несите WMI на территорию, у которой уже есть выделенный механизм (настройки, счётчики производительности, разовые вызовы API) — WMI/CIM оставляйте сквозным и удалённым запросам. В одной фразе это и есть место, где он уместен.

Похожие статьи

Смежные области консультирования

KomuraSoft LLC занимается встраиванием в бизнес-приложения получения сведений об оборудовании, мониторинга процессов и запросов к удалённым ПК через WMI/CIM, миграцией внутренних скриптов на Get-WmiObject на командлеты CIM и разбором причин вроде «на машине разработки работает, а у заказчика падает с ошибкой прав». Можно пройти весь путь от прототипа в PowerShell до основной реализации на C#.

Справочные ссылки

  1. Microsoft Learn, About WMI. О том, что WMI — реализация Microsoft WBEM (отраслевой инициативы по разработке стандартных технологий доступа к сведениям управления в корпоративной среде); что объекты управления представляются отраслевым стандартом CIM (Common Information Model), который разрабатывает и поддерживает DMTF (Distributed Management Task Force); что следующее поколение MI (Windows Management Infrastructure) полностью совместимо с прежним WMI; и что удалённое подключение WMI идёт по DCOM, а альтернатива — WinRM на базе WS-Management.  2 3 4 5

  2. Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. О том, что командлеты WMI v1 (Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject) удалены из PowerShell, и что командлеты модуля CimCmdlets (WMI v2) дают ту же функциональность с новыми возможностями и переработанным синтаксисом.  2

  3. Microsoft Learn, Get-CimInstance (CimCmdlets). О том, что без ComputerName и CimSession подключение идёт к локальному WMI сессией COM, а при -ComputerName создаётся временная сессия по протоколу WsMan; что при нескольких операциях против одного компьютера по производительности рекомендуется подключение через сессию CIM; что -Filter — предложение where WQL/CQL без ключевого слова WHERE; что -Property и -KeyOnly уменьшают размер объектов и сетевой трафик; что пространство имён по умолчанию — root/CIMV2, а язык запросов по умолчанию (-QueryDialect) — WQL; что вывод — Microsoft.Management.Infrastructure.CimInstance; о примере вызова GetOwner в связке с Invoke-CimMethod; и о том, что командлет только для Windows.  2 3 4 5 6 7 8 9 10

  4. Microsoft Learn, New-CimSessionOption (CimCmdlets). О том, что у параметров сессии CIM два набора — для WsMan и для DCOM; что -Protocol принимает Dcom / Default / Wsman; о примере создания сессии CIM по DCOM передачей в -SessionOption у New-CimSession параметра, созданного New-CimSessionOption -Protocol Dcom; и о том, что уровень олицетворения по умолчанию для сессии DCOM — Impersonate.  2

  5. Microsoft Learn, ManagementObjectSearcher Class (System.Management). О том, что это самый обычный класс входа для получения сведений управления: по заданному запросу WQL он получает коллекцию объектов управления; что принимает ObjectQuery и ManagementScope (пространство имён WMI) и возвращает ManagementObjectCollection через Get(); и что System.Management.dll поставляется как пакет NuGet System.Management.  2 3 4

  6. Microsoft Learn, CimSession Class (Microsoft.Management.Infrastructure). О том, что Microsoft.Management.Infrastructure.dll поставляется как пакет NuGet Microsoft.Management.Infrastructure; о создании сессии через Create(computerName); о выполнении запроса через QueryInstances(namespace, queryDialect, query); и о том, что есть EnumerateInstances / GetInstance / InvokeMethod / Subscribe вместе с асинхронными вариантами каждого (*Async), при реализации IDisposable.  2 3 4 5

  7. Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). О том, что на indication (событие) подписываются по имени класса или выражению запроса и именуют подписку через -SourceIdentifier; о примере подписки на Win32_ProcessStartTrace с примечанием, что выполнение требует запуска PowerShell от администратора; о примере обращения к ProcessName / ProcessId из $Event.SourceEventArgs.NewEvent внутри скрипт-блока -Action; о том, что при -ComputerName подключение идёт временной сессией WsMan, а без указания — локально по COM; и о снятии подписки через Unregister-Event.  2 3 4

  8. Microsoft Learn, Win32_ProcessStartTrace class. О том, что это класс события, указывающий на запуск нового процесса, со свойствами ProcessName / ProcessID / ParentProcessID / SessionID / Sid; что свойство SECURITY_DESCRIPTOR — дескриптор, по которому поставщик события решает, какие пользователи могут его получать; и что пространство имён — Root\CIMV2, предоставляет поставщик трассировки ядра (Krnlprov.dll).  2 3

  9. Microsoft Learn, Win32_LogicalDisk class. О том, что Win32_LogicalDisk — класс, производный от CIM_LogicalDisk, представляющий локальное устройство хранения; о значениях DriveType (2 = съёмный, 3 = локальный диск, 4 = сетевой диск, 5 = CD и т. д.); что FreeSpace / Size — значения uint64 в байтах; что DeviceID — ключ; и о примерах запросов VBScript / C#, фильтрующих по DriveType = 3.  2 3 4

  10. Microsoft Learn, Installation and configuration for Windows Remote Management. О том, что по умолчанию прослушиватель WinRM не настроен и сообщения WS-Management нельзя ни отправить, ни принять; что winrm quickconfig ставит службе автозапуск, настраивает прослушиватель HTTP/HTTPS и регистрирует исключение брандмауэра; что порты по умолчанию WinRM 2.0 — HTTP 5985 / HTTPS 5986; что когда взаимную проверку подлинности (Kerberos) установить нельзя, например в рабочей группе, TrustedHosts задают как можно уже; и о дескрипторе безопасности по умолчанию (RootSDDL), который контролирует удалённый доступ к прослушивателю, плюс дополнительной настройке, чтобы разрешить пользователям не из администраторов пользоваться подключаемыми модулями WMI.  2 3 4

  11. Microsoft Learn, System.Management Namespace. О том, что это пространство имён, которое запрашивает инфраструктуру WMI семейством классов ManagementObjectSearcher и обрабатывает подписку на события через ManagementEventWatcher; что WqlEventQuery представляет событийный запрос в форме WQL; и что ManagementDateTimeConverter даёт методы взаимного преобразования представлений даты/времени и интервала DMTF и DateTime / TimeSpan среды CLR.  2 3

  12. Microsoft Learn, Requesting WMI Data on a 64-bit Platform. О том, что при сосуществовании 32-битной и 64-битной версий поставщика по умолчанию 32-битным приложениям (включая скрипты) отвечает 32-битный поставщик, а 64-битным — 64-битный; что через __ProviderArchitecture (32 или 64) и __RequiredArchitecture в контексте можно запросить или принудить нестандартную версию поставщика (при принуждении к отсутствующей версии возникает WBEM_E_PROVIDER_LOAD_FAILURE); и на примере поставщика реестра — что 32-битный клиент получает данные стороны HKLM\SOFTWARE\Wow6432Node.  2 3

  13. Microsoft Learn, winmgmt. О том, что /verifyrepository у winmgmt.exe проверяет целостность репозитория WMI; что /salvagerepository проверяет целостность и при обнаружении несогласованности перестраивает репозиторий, сливая содержимое, которое удалось прочитать; что /resetrepository возвращает репозиторий к состоянию начальной установки ОС; что репозиторий работает как база данных из набора файлов в папке Repository; и что ошибки, всплывающие через WMI, иногда происходят из другой части ОС, поэтому удаление репозитория как первая реакция недопустимо: оно может привести к повреждению системы или установленных приложений.  2 3

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Японская эра, праздники и даты закрытия периода в бизнес-приложениях — устойчивый к смене эры дизайн, JapaneseCalendar и расчёт рабочих дней на практике

Показать «Рэйва 8» в отчёте, рассчитать рабочие дни без учёта праздников, оплатить в последний рабочий день месяца, следующего за закрыти...

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Чем WMI отличается от CIM?
CIM — это «отраслевая стандартная модель для представления объектов управления: систем, устройств и т. п.», которую разрабатывает и поддерживает DMTF (Distributed Management Task Force). WMI — реализация Microsoft инициативы WBEM, которая пользуется этим стандартом, и она встроена в Windows. Иначе говоря, CIM — спецификация, а WMI — её реализация на Windows. Get-CimInstance в PowerShell и Microsoft.Management.Infrastructure в C# называются «CIM» потому, что это API, следующие стандарту; точка подключения при этом та же инфраструктура WMI. Для повседневной разработки достаточно понимать это как «запрашивать классы WMI (Win32_* и подобные) через API семейства CIM».
Get-WmiObject больше нельзя использовать?
В Windows PowerShell 5.1 он по-прежнему работает, но начиная с PowerShell 6 (включая актуальный PowerShell 7) командлеты WMI v1 — Get-WmiObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance и Remove-WmiObject — удалены и не запускаются. Ту же функциональность даёт модуль CimCmdlets (Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent и другие). Новые скрипты безопаснее писать командлетами CIM даже если они будут работать под 5.1. Тогда при переходе на PowerShell 7 переписывать часть, связанную с WMI, не придётся.
Чтобы обращаться к WMI из C#, что брать: System.Management или Microsoft.Management.Infrastructure?
Оба API только для Windows, и в актуальном .NET их подключают как пакеты NuGet. System.Management — классический API: достаточно передать WQL в ManagementObjectSearcher, и этого хватает, если в центре — получение локальных сведений. Там же есть ManagementDateTimeConverter для преобразования дат DMTF. Microsoft.Management.Infrastructure (API MI), напротив, разделяет ту же систему типов, что и командлеты CIM в PowerShell (CimSession / CimInstance), и единообразно закрывает удалённые запросы по WSMan, асинхронные варианты методов и подписку на события (Subscribe). Если в продукт всерьёз встраиваются запросы к удалённым ПК и мониторинг, разумно выбрать API MI.
Get-CimInstance не подключается к удалённому ПК. Что проверить?
Сначала убедитесь, что на целевой машине настроен WinRM. Операция CIM с -ComputerName создаёт временную сессию по протоколу WSMan (WinRM), поэтому на цели должны работать служба WinRM и прослушиватель. winrm quickconfig выполняет конфигурацию по умолчанию (запуск службы, создание прослушивателя, исключение в брандмауэре). Порты по умолчанию — 5985 для HTTP и 5986 для HTTPS, так что проверьте и брандмауэры на пути. В рабочей группе взаимная проверка подлинности через Kerberos недоступна, поэтому может понадобиться регистрация цели в TrustedHosts на клиенте. Если WinRM на цели никак не настроить, можно подключиться по DCOM через параметр, созданный New-CimSessionOption -Protocol Dcom.
Почему дата WMI возвращается в формате вроде «20260801100000.000000+540»?
Даты WMI хранятся в строковом формате спецификации CIM от DMTF (yyyymmddHHMMSS.mmmmmm±UUU, суффикс — смещение от UTC в минутах). Если читать сырое значение старым Get-WmiObject или через System.Management, эта строка возвращается как есть. В C# (System.Management) ManagementDateTimeConverter даёт взаимное преобразование формата DMTF и DateTime / TimeSpan — пользуйтесь им, а не разрезайте строку вручную. Если же данные получены API семейства CIM вроде Get-CimInstance, свойства дат уже возвращаются преобразованными в DateTime, и с этой проблемой вы вообще не столкнётесь.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог