WMI/CIM из C# и PowerShell — практическое руководство по сведениям об оборудовании, мониторингу процессов и удалённым запросам
· Го Комура · Windows, C#, .NET, PowerShell, WMI, CIM, Бизнес-приложения, Разработка для Windows
«Хочу показать серийный номер и модель ПК на экране бизнес-приложения.» «Хочу следить за свободным местом на диске сервера и поднимать предупреждение.» «Хочу обнаруживать, что запустился конкретный процесс.» «Хочу собрать состояние ПК в другом месте в одном запросе.» — такие требования в разработке бизнес-приложений и средств администрирования для Windows встречаются постоянно. И стандартный ответ на них — WMI (Windows Management Instrumentation), или, стандартным именем, CIM (Common Information Model).
flowchart TB
accTitle: Типичные требования и WMI/CIM
accDescr: Показать серийный номер и модель, следить за свободным местом на диске, обнаруживать запуск процессов и запрашивать удалённые ПК — типичные требования бизнес-приложений, стандартный ответ на которые есть WMI, а стандартное имя — CIM
r1["Серийный номер и модель"] --> ans["WMI(стандартное имя — CIM)"]
r2["Мониторинг свободного места на диске"] --> ans
r3["Обнаружение запуска процессов"] --> ans
r4["Запрос к удалённым ПК"] --> ans
Рис. 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 |
flowchart TB
accTitle: Связь стандарта CIM и реализации WMI
accDescr: Стандарт CIM, который разрабатывает и поддерживает DMTF, используют в рамках инициативы WBEM, реализация Microsoft — WMI, следующее поколение MI полностью совместимо с прежним WMI, а API семейства CIM подключаются к той же инфраструктуре WMI
dmtf["Разрабатывает и поддерживает DMTF"] --> cim["CIM(отраслевая стандартная модель)"]
wbem["WBEM(отраслевая инициатива)"] --> wmi["WMI(реализация Microsoft)"]
cim --> wmi
wmi -.-> mi["MI(следующее поколение, полная совместимость)"]
api["API семейства CIM(PowerShell / C#)"] --> wmi
Рис. 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; это не механизм, который умеет всё.
flowchart TB
accTitle: Структура запроса WMI
accDescr: Запрос WQL направлен на классы Win32_* внутри пространства имён root/CIMV2, поставщик, который даёт содержимое класса, на месте спрашивает ОС и собирает значения, после чего возвращается результат
wql["Запрос на WQL"] --> ns["Пространство имён root/CIMV2"]
ns --> cls["Класс Win32_*"]
cls --> prov["Поставщик"]
prov --> osq["На месте спрашивает ОС"]
osq --> res["Возвращает результат"]
Рис. 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.
flowchart TB
accTitle: Вызов методов CimInstance
accDescr: CimInstance, который возвращает Get-CimInstance, отдаёт свойства дат уже преобразованными в DateTime, но методов напрямую не имеет, поэтому вызов метода делают, передавая экземпляр в Invoke-CimMethod
gci["Get-CimInstance"] --> inst["Объект CimInstance"]
inst -.-> dt["Даты уже преобразованы в DateTime"]
inst -.-> nom["Методов напрямую нет"]
inst --> icm["Передать в Invoke-CimMethod"]
icm --> call["Вызов метода"]
Рис. 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».
flowchart TB
accTitle: Зачем писать новые скрипты на CIM
accDescr: Скрипт на командлетах WMI в 5.1 работает, но начиная с PowerShell 6 они удалены, поэтому при миграции нужна переписка, а командлеты CIM работают и в 5.1, так что если новое писать на CIM, стоимости миграции не остаётся
new["Новый скрипт"] --> q1{"На чём писать?"}
q1 -->|Командлеты WMI| old["В 5.1 работает"]
q1 -->|Командлеты CIM| cur["Работает и в 5.1"]
old --> del["В PowerShell 7 удалены"]
del --> rew["Переписка при миграции"]
cur --> norew["Стоимости миграции не остаётся"]
Рис. 5: Если новое писать командлетами CIM, при переходе на PowerShell 7 переписывать не придётся.
4. Удалённые запросы — сессии CIM (WSMan по умолчанию) и параметр DCOM
Командлеты CIM без указания цели подключаются к локальному WMI по COM; если указать -ComputerName, они подключаются, создавая временную сессию по протоколу WSMan (WinRM). Когда против одного компьютера делают несколько операций, по производительности выгоднее создать сессию CIM и использовать её повторно.3
flowchart TB
accTitle: Выбор способа подключения CIM
accDescr: Без указания — подключение COM к локальному WMI, при ComputerName каждый запрос создаёт временную сессию WSMan, несколько операций к одному узлу выгоднее гонять через повторное использование New-CimSession, а для цели без настроенного WinRM есть параметр протокола DCOM
exec["Выполнение командлета CIM"] --> q1{"Указан ComputerName?"}
q1 -->|нет| local["Подключение COM к локальному WMI"]
q1 -->|да| q2{"Несколько операций к одному узлу?"}
q2 -->|разовая| temp["Временная сессия WSMan"]
q2 -->|несколько| sess["Повторно использовать New-CimSession"]
temp -.-> cost["Создаётся на каждый запрос"]
nowinrm["Цель без настроенного WinRM"] -.-> dcom["Параметр протокола 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 значением по умолчанию.
flowchart TB
accTitle: Проверка предпосылок удалённого запроса
accDescr: На цели winrm quickconfig за один проход ставит службе автозапуск, создаёт прослушиватель HTTP и регистрирует исключение брандмауэра, прослушиватель HTTPS настраивают отдельно с сертификатом, а в рабочей группе может понадобиться регистрация в TrustedHosts
qc["winrm quickconfig"] --> svc["Автозапуск службы"]
qc --> lis["Создание прослушивателя HTTP(5985)"]
qc --> fw["Исключение брандмауэра"]
lis ~~~ https["Прослушиватель HTTPS(5986)"]
https -.-> cert["Готовят сертификат и настраивают отдельно"]
fw ~~~ wg["Среда рабочей группы"]
wg -.-> th["Регистрация в 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.
flowchart TB
accTitle: Ловушка получения свойства и приведения типа
accDescr: В System.Management свойства возвращаются как object через индексатор, поэтому нужно свериться с типом CIM в документации класса и привести, а если решить что это int и привести, получится InvalidCastException
idx["Получение через индексатор"] --> obj["Возвращается как object"]
obj --> chk["Сверка типа CIM в документации"]
chk --> cast["Приведение к верному типу"]
obj -.-> wrong["Решили что int и привели"]
wrong -.-> ex["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) и получить результат в виде объектов».
flowchart TB
accTitle: Поток от пробы в PowerShell к переносу в C#
accDescr: Командлеты CIM в PowerShell и API MI в C# работают с одним типом CimInstance, поэтому поток разработки — прототип в PowerShell, затем перенос в C# — стыкуется естественно
trial["Прототип в PowerShell"] --> gci["Командлеты CIM"]
impl["Основная реализация на C#"] --> mi["API MI"]
gci --> ci["Один тип CimInstance"]
mi --> ci
ci -.-> flow["Перенос стыкуется естественно"]
Рис. 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 — и получится основа дискового мониторинга без агента.
flowchart TB
accTitle: Основа дискового мониторинга без агента
accDescr: Фильтр DriveType 3 исключает съёмные, сетевые и CD и оставляет только локальные диски, тот же скрипт гоняют по серверам через сессию CIM и получают основу дискового мониторинга без агента
scr["Скрипт свободного места"] --> flt["Сузить DriveType = 3"]
flt -.-> exc["Исключить съёмные и прочее"]
scr --> ses["Через сессию CIM"]
ses --> srvs["Прогнать по серверам"]
srvs --> mon["Мониторинг без агента"]
Рис. 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
sequenceDiagram
accTitle: Поток подписки на событие запуска процесса
accDescr: В сессии PowerShell с правами администратора Register-CimIndicationEvent регистрирует подписку, при каждом запуске процесса приходит событие и выполняется Action, а по окончании мониторинга снимают через Unregister-Event
participant ps as Сессия PowerShell
participant wmi as WMI
ps->>wmi: Регистрация подписки через Register-CimIndicationEvent
Note over ps: Запуск с правами администратора
wmi-->>ps: Событие при каждом запуске процесса
ps->>ps: Выполнение -Action
ps->>wmi: Снятие через Unregister-Event(по окончании мониторинга)
Рис. 11: Подписка действует, пока жива зарегистрировавшая её сессия; снятие делают, когда мониторинг закончен.
Другой способ — универсальное событие создания экземпляра (__InstanceCreationEvent), применимое к любому классу. Здесь WMI опрашивает с интервалом, заданным WITHIN, и превращает разницу в события, поэтому компромисс между интервалом обнаружения и нагрузкой выбираете вы сами.
flowchart TB
accTitle: Два способа подписки для обнаружения запуска процессов
accDescr: Win32_ProcessStartTrace — способ подписаться на класс события поставщика трассировки ядра, универсальный __InstanceCreationEvent опрашивает с интервалом WITHIN и превращает разницу в события, поэтому компромисс между интервалом обнаружения и нагрузкой выбираете сами
goal["Обнаружение запуска процессов"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["Подписка на трассировку ядра"]
t2 -.-> w1["Опрос с интервалом WITHIN"]
w1 -.-> tr["Компромисс интервала и нагрузки"]
Рис. 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
Если встраиваете это в постоянно работающий мониторинг, заложите в проект и повторную регистрацию подписки, когда она обрывается (при перезапуске службы, при ошибке). Проектирование «проверки и отображения состояния», включая мониторинг устройств, разобрано в «Лучшие практики проверки и отображения состояния внешних устройств».
stateDiagram-v2
accTitle: Жизненный цикл подписки в постоянном мониторинге
accDescr: В постоянном мониторинге состояние активной подписки может оборваться из-за перезапуска службы или ошибки, поэтому в проект включают обнаружение обрыва, повторную регистрацию и возврат к активной подписке
s1: Подписка активна
s2: Подписка оборвалась
s3: Повторная регистрация
[*] --> s1
s1 --> s2: Перезапуск службы / ошибка
s2 --> s3
s3 --> s1
Рис. 13: В постоянном мониторинге проект должен включать повторную регистрацию и возврат к активной подписке, когда она обрывается.
7. Ловушки — производительность, права, 64-бит, репозиторий и даты
7.1. SELECT * и слишком частый опрос
Запрос WMI — это «поставщик собирает значения на месте», и он не бесплатен. Два классических антипаттерна.
- Привычное использование
SELECT *. Сбор всех свойств всех строкWin32_Processраздувает работу поставщика и сетевую передачу (при удалённом доступе). Сужайте строки-Filter, столбцы-Property, а если нужны только ключи для последующей операции —-KeyOnly. Всё это официальные средства «чтобы уменьшить размер объектов и сетевой трафик».3 - Короткий цикл опроса. Конструкцию вроде «каждую секунду
Get-CimInstance Win32_Process» заменяют подпиской на события из раздела 6.3. Даже если без опроса (WITHIN) не обойтись, интервал расширяют до действительно достаточного для требования.
Кроме того, повторять -ComputerName по одной машине против удалённых узлов тоже расточительно: временная сессия создаётся на каждый запрос. Несколько операций переводят на повторное использование сессии CIM.3
flowchart TB
accTitle: Антипаттерны производительности и чем их заменить
accDescr: Привычное SELECT со звёздочкой сужают Filter и Property по строкам и столбцам, для одних ключей берут KeyOnly, короткий цикл опроса заменяют подпиской на события, а повтор ComputerName по одной машине — повторным использованием сессии CIM
a1["Привычное SELECT *"] --> f1["Сузить Filter и Property"]
f1 -.-> f2["Только ключи — KeyOnly"]
a2["Короткий цикл опроса"] --> f3["Заменить подпиской на события"]
a3["ComputerName по одной машине"] --> f4["Повторно использовать сессию CIM"]
Рис. 14: Сужение строк, столбцов и ключей плюс замена опроса подпиской на события предотвращают половину проблем производительности WMI.
7.2. Права для подписки на события
Как в разделе 6.3, подписка семейства Win32_ProcessStartTrace предполагает права администратора.7 Авария «на машине разработки (запуск от администратора) работало, а в среде обычного пользователя у заказчика мониторинг не работает» — классика рядом с диалогом уведомления брандмауэра. Если мониторинг встраивают в бизнес-приложение, которое работает от обычного пользователя, имеет смысл вынести часть мониторинга в службу Windows (например от LocalSystem) и связать её с основным приложением межпроцессным взаимодействием.
flowchart TB
accTitle: Схема мониторинга в среде обычного пользователя
accDescr: Подписку, которая требует прав администратора, выносят из основного приложения, работающего от обычного пользователя, изолируя мониторинг в службе Windows от LocalSystem или подобного, и связывают с основным приложением межпроцессным взаимодействием
svcm["Служба Windows для мониторинга"] --> subm["Подписка на трассировку запуска"]
svcm -.-> lsm["Запуск от LocalSystem и т. п."]
appm["Основное приложение(обычный пользователь)"] ---|Межпроцессное взаимодействие| svcm
Рис. 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».
flowchart TB
accTitle: Выбор поставщика в 64-битной среде
accDescr: По умолчанию отвечает поставщик, чья разрядность совпадает с вызывающим приложением, запрос реестра из 32-битного приложения получает значение стороны Wow6432Node, но указанием __ProviderArchitecture можно явно запросить противоположное представление
q1{"Разрядность вызывающего?"} -->|32bit| p32["Отвечает 32-битный поставщик"]
q1 -->|64bit| p64["Отвечает 64-битный поставщик"]
p32 -.-> wow["Реестр — значение стороны Wow6432Node"]
ctx["Указание __ProviderArchitecture"] -.-> ov["Явно запросить противоположное представление"]
Рис. 16: По умолчанию отвечает сторона, совпадающая с разрядностью вызывающего, поэтому 32-битное приложение читает сторону Wow6432Node.
7.4. Симптомы и лечение повреждённого репозитория WMI
Определения классов WMI хранятся в репозитории (это не один файл: набор файлов в папке Repository работает как база данных13). Когда он становится несогласованным, начинают появляться ошибки вроде «класс, который должен существовать, не найден» или «пространство имён недействительно», хотя на стороне приложения ничего не меняли. Для диагностики и восстановления используют winmgmt.exe.13
rem Проверка целостности (результат inconsistent значит есть проблема)
winmgmt /verifyrepository
rem Проверка целостности и перестройка при проблеме (читаемое содержимое сливается)
winmgmt /salvagerepository
Важно не делать удаление или сброс репозитория первым шагом. Ошибки, которые всплывают через WMI, могут происходить из другой части ОС, и Microsoft прямо пишет, что удаление репозитория как первая реакция «может привести к повреждению системы или установленных приложений».13 Соблюдайте порядок: проверка /verifyrepository, затем восстановление /salvagerepository.
flowchart TB
accTitle: Порядок разбора несогласованности репозитория WMI
accDescr: Если появляются ошибки вроде «класс не найден», проверяют целостность winmgmt verifyrepository, при несогласованности перестраивают salvagerepository, и не делают удаление или сброс репозитория первым шагом
sym["Ошибки вроде «класс не найден»"] --> verify["winmgmt /verifyrepository"]
verify --> q1{"Результат inconsistent?"}
q1 -->|да| salvage["winmgmt /salvagerepository"]
q1 -->|нет| other["Искать причину в другой части ОС"]
salvage -.-> merge["Читаемое содержимое сливается"]
del["Удаление или сброс репозитория"] -.-> ng["Не первым шагом"]
Рис. 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в расчётах.
flowchart TB
accTitle: Обращение с форматом даты DMTF
accDescr: Даты WMI хранятся строкой формата DMTF, сырое значение из System.Management преобразуют ManagementDateTimeConverter, API семейства CIM возвращают уже DateTime, поэтому строку вручную не разрезают
dmtf["Строка формата DMTF"] --> q1{"Каким API получено?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|API семейства CIM| done["Возвращается уже как DateTime"]
conv --> dtv["Преобразование в DateTime / TimeSpan"]
cut["Ручная нарезка строки"] -.-> ng["Не использовать"]
Рис. 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, не трогая его напрямую».
flowchart TB
accTitle: Ось выбора средства
accDescr: Ось выбора — пользоваться выделенным механизмом там, где он есть, а WMI и CIM оставлять сквозным и удалённым запросам там, где его нет, специализированные командлеты на базе CIM — форма получить пользу, не трогая WMI и CIM напрямую
q1{"Есть выделенный механизм?"} -->|есть| ded["Пользоваться выделенным механизмом"]
q1 -->|нет| wmi["Пользоваться WMI / CIM"]
wmi -.-> use["Сквозные и удалённые запросы"]
cmd["Специализированные командлеты на базе CIM"] -.-> ben["Форма получить только пользу"]
Рис. 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 оставляйте сквозным и удалённым запросам. В одной фразе это и есть место, где он уместен.
Похожие статьи
- Как запустить PowerShell из C# (CSharp) и получить результат в виде объектов
- Практические команды PowerShell — расширяем набор маленьких инструментов для повседневной работы
- Различия между Windows PowerShell 5.1 и PowerShell 7 — практическое руководство по миграции внутренних скриптов
- Лучшие практики проверки и отображения состояния внешних устройств — проектирование за пределами одного «подключено»
- Безопасный вызов Win32 API из C# — практическое руководство по P/Invoke (DllImport / LibraryImport / CsWin32)
- Что такое TPM в Windows — наглядно о «сейфе, который не выпускает ключи» и measured boot
Смежные области консультирования
KomuraSoft LLC занимается встраиванием в бизнес-приложения получения сведений об оборудовании, мониторинга процессов и запросов к удалённым ПК через WMI/CIM, миграцией внутренних скриптов на Get-WmiObject на командлеты CIM и разбором причин вроде «на машине разработки работает, а у заказчика падает с ошибкой прав». Можно пройти весь путь от прототипа в PowerShell до основной реализации на C#.
- Разработка приложений для Windows
- Расследование ошибок и причин
- Технические консультации и ревью дизайна
- Связаться с нами
Справочные ссылки
-
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
-
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
-
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
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). О том, что у параметров сессии CIM два набора — для WsMan и для DCOM; что -Protocol принимает Dcom / Default / Wsman; о примере создания сессии CIM по DCOM передачей в -SessionOption у New-CimSession параметра, созданного New-CimSessionOption -Protocol Dcom; и о том, что уровень олицетворения по умолчанию для сессии DCOM — Impersonate. ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). О том, что это самый обычный класс входа для получения сведений управления: по заданному запросу WQL он получает коллекцию объектов управления; что принимает ObjectQuery и ManagementScope (пространство имён WMI) и возвращает ManagementObjectCollection через Get(); и что System.Management.dll поставляется как пакет NuGet System.Management. ↩ ↩2 ↩3 ↩4
-
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
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). О том, что на indication (событие) подписываются по имени класса или выражению запроса и именуют подписку через -SourceIdentifier; о примере подписки на Win32_ProcessStartTrace с примечанием, что выполнение требует запуска PowerShell от администратора; о примере обращения к ProcessName / ProcessId из $Event.SourceEventArgs.NewEvent внутри скрипт-блока -Action; о том, что при -ComputerName подключение идёт временной сессией WsMan, а без указания — локально по COM; и о снятии подписки через Unregister-Event. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Win32_ProcessStartTrace class. О том, что это класс события, указывающий на запуск нового процесса, со свойствами ProcessName / ProcessID / ParentProcessID / SessionID / Sid; что свойство SECURITY_DESCRIPTOR — дескриптор, по которому поставщик события решает, какие пользователи могут его получать; и что пространство имён — Root\CIMV2, предоставляет поставщик трассировки ядра (Krnlprov.dll). ↩ ↩2 ↩3
-
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
-
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
-
Microsoft Learn, System.Management Namespace. О том, что это пространство имён, которое запрашивает инфраструктуру WMI семейством классов ManagementObjectSearcher и обрабатывает подписку на события через ManagementEventWatcher; что WqlEventQuery представляет событийный запрос в форме WQL; и что ManagementDateTimeConverter даёт методы взаимного преобразования представлений даты/времени и интервала DMTF и DateTime / TimeSpan среды CLR. ↩ ↩2 ↩3
-
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
-
Microsoft Learn, winmgmt. О том, что /verifyrepository у winmgmt.exe проверяет целостность репозитория WMI; что /salvagerepository проверяет целостность и при обнаружении несогласованности перестраивает репозиторий, сливая содержимое, которое удалось прочитать; что /resetrepository возвращает репозиторий к состоянию начальной установки ОС; что репозиторий работает как база данных из набора файлов в папке Repository; и что ошибки, всплывающие через WMI, иногда происходят из другой части ОС, поэтому удаление репозитория как первая реакция недопустимо: оно может привести к повреждению системы или установленных приложений. ↩ ↩2 ↩3
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Практические лучшие практики многопоточности: издание .NET — что решить, прежде чем добавлять потоки
Практический разбор правил проектирования, которые не дают многопоточному коду на .NET/C# «изредка падать или зависать»: не создавать пот...
Как запустить PowerShell из C# (CSharp) и получить результат в виде объектов
Разбираем, как запустить PowerShell из C# и получать результаты не строками, а объектами PSObject, — от PowerShell SDK, AddCommand и AddP...
Японская эра, праздники и даты закрытия периода в бизнес-приложениях — устойчивый к смене эры дизайн, JapaneseCalendar и расчёт рабочих дней на практике
Показать «Рэйва 8» в отчёте, рассчитать рабочие дни без учёта праздников, оплатить в последний рабочий день месяца, следующего за закрыти...
Как понимать изоляцию сеансов Windows — Session 0, RDP и одновременная работа нескольких пользователей
В этой статье разбирается понятие «сеанса» (session) в Windows — тема, которая постоянно сбивает с толку разработчиков Windows-приложений...
Защита от повторного запуска приложения Windows — именованный Mutex и активация окна при повторном запуске
Разбираем, как реализовать защиту бизнес-приложения Windows от повторного запуска с помощью именованного Mutex: подводные камни RDP-окруж...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Чем 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, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.