WMI/CIM을 C#・PowerShell에서 사용하기 ── 하드웨어 정보 취득・프로세스 감시・원격 조회 실무 가이드

· · Windows, C#, .NET, PowerShell, WMI, CIM, 업무 앱, Windows 개발

「PC의 시리얼 번호와 기종명을 업무 앱 화면에 표시하고 싶다」「서버의 디스크 여유 공간을 감시해서 경고하고 싶다」「특정 프로세스가 시작된 것을 감지하고 싶다」「떨어진 곳에 있는 PC의 상태를 한꺼번에 조회하고 싶다」── Windows 업무 앱이나 관리 도구 개발에서 이런 종류의 요구는 정석입니다. 그리고 그 정석의 답이 WMI(Windows Management Instrumentation), 표준 명칭으로 말하면 CIM(Common Information Model)입니다.

까다로운 점은 WMI 관련 정보가 신구가 뒤섞여 있다는 것입니다. 검색하면 Get-WmiObject를 사용한 10년 전 글과 Get-CimInstance를 사용한 글이 함께 나오고, C# 쪽도 System.ManagementMicrosoft.Management.Infrastructure 두 계통이 있습니다. 어느 쪽이 현행 작성 방식이고 어느 쪽이 「지금도 동작하지만 새로 작성할 때는 선택하지 않는」 방식인지 알기 어려운 상태입니다. 실제로 Get-WmiObject는 PowerShell 7에 존재하지 않아, 5.1용으로 작성한 사내 스크립트를 이전할 때 갑자기 표면화됩니다.

이 글에서는 업무 앱에서 하드웨어 정보 취득・프로세스 감시・원격 PC 조회를 구현하는 C#/PowerShell 개발자를 대상으로, WMI/CIM 구조에 대한 최소한의 이해부터 PowerShell의 CIM 명령어, C#의 두 가지 API, 자주 쓰는 실전 레시피, 성능・권한・64bit의 함정, 그리고 「WMI를 사용해서는 안 되는 상황」의 판단까지를 2026년 8월 시점의 1차 정보를 바탕으로 정리합니다.

1. 먼저 결론

  • CIM은 DMTF가 책정하는 관리 정보의 업계 표준이며, WMI는 그 Microsoft 구현체입니다. PowerShell이나 C#의 「CIM」 계열 API는 이 표준을 따르는 현행 세대의 API로, 접속 대상은 동일한 WMI 기반입니다.1
  • PowerShell의 현행은 CIM 명령어(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent)입니다. 구형 WMI 명령어(Get-WmiObject 등 5개)는 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으로 도입합니다. 원격이나 감시를 본격적으로 하려면 CIM 명령어와 동일한 타입 체계를 가진 MI API가 적합합니다.56
  • 프로세스 시작 감지는 이벤트 구독으로 하고, 폴링으로 하지 않을 것. Win32_ProcessStartTrace의 구독은 관리자 권한으로 실행해야 합니다.78
  • SELECT *를 습관적으로 사용하지 말 것. -Filter / -Property / -KeyOnly로 전송할 데이터를 좁히는 것이 WMI 성능 문제의 절반을 예방합니다.3
  • WMI는 만능이 아닙니다. 고빈도 성능 감시, 자체 앱의 설정 읽기・쓰기, 단발성 OS 기능 호출에는 성능 카운터・레지스트리・Win32 API・전용 명령어 쪽이 적합합니다(8장의 판단표).

2. WMI/CIM이란 무엇인가 ── 표준과 구현, 네임스페이스, 클래스, WQL

먼저 용어의 관계를 한 번에 정리합니다.

용어 정체
CIM (Common Information Model) 시스템・애플리케이션・네트워크・디바이스 등 관리 대상을 표현하는 업계 표준 모델. DMTF(Distributed Management Task Force)가 책정・유지1
WBEM (Web-Based Enterprise Management) 기업 환경의 관리 정보에 접근하는 표준 기술을 만드는 업계 이니셔티브1
WMI WBEM의 Microsoft 구현체. CIM 표준을 사용해 관리 대상을 표현하며, Windows에 내장되어 있음1
MI (Windows Management Infrastructure) WMI의 차세대판. 기존 WMI와 완전 호환되며, 새로운 프로바이더 대부분이 MI로 작성됨1

개발자로서 파악해 두어야 할 구조는 다음 네 가지입니다.

  • 네임스페이스(namespace): 클래스를 묶는 계층입니다. 일상적인 조회에서 사용하는 것은 거의 root/CIMV2이며, CIM 명령어의 기본값도 여기입니다.3 그 외에 root\default(레지스트리 프로바이더 등) 등이 있습니다.
  • 클래스: Win32_ComputerSystem(컴퓨터 본체), Win32_LogicalDisk(논리 드라이브), Win32_Process(프로세스)와 같은 관리 대상의 타입입니다. CIM 표준 클래스(CIM_LogicalDisk 등)를 상속한 Windows 고유 클래스가 Win32_ 접두사를 가집니다.9
  • 프로바이더: 클래스의 실체를 제공하는 컴포넌트입니다. 조회하면 프로바이더가 그 자리에서 OS에 문의해 값을 만듭니다.
  • WQL: SQL과 비슷한 조회 언어입니다. SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto'와 같이 클래스를 테이블에 빗대어 좁힙니다. CIM 명령어의 기본 조회 언어도 WQL입니다.3

「OS나 하드웨어의 정보를 통일된 클래스와 쿼리 언어로 읽을 수 있다」── 이것이 WMI의 가치입니다. 반대로 말하면 쓰기나 조작은 Invoke-CimMethod로 호출할 수 있는 메서드를 가진 일부 클래스에 한정되며, 무엇이든 할 수 있는 구조는 아닙니다.

3. PowerShell에서의 이용 ── CIM 명령어가 현행, WMI 명령어는 삭제됨

3.1. 기본은 Get-CimInstance

# 클래스 지정(기본 네임스페이스 root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem

# WHERE절만 -Filter에 작성(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는 WQL의 WHERE절 그 자체이고, -Property는 취득할 열의 한정입니다.3 반환값은 CimInstance 객체이며, 날짜 속성(CreationDateLastBootUpTime 등)은 DateTime으로 변환된 상태로 반환됩니다. 구형 Get-WmiObject와 달리 취득한 객체는 메서드를 직접 가지지 않기 때문에, 메서드 호출은 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 인스턴스 삭제

Windows PowerShell 5.1에서도 CIM 명령어를 사용할 수 있으므로, 새로 작성하는 것은 5.1에서 돌릴 경우라도 CIM 쪽으로 작성하는 것이 이전 비용을 남기지 않는 방법입니다. 5.1과 7의 공존・이전 전체상은 「Windows PowerShell 5.1과 PowerShell 7의 차이」에 정리해 두었습니다.

4. 원격 조회 ── CIM 세션(WSMan 기본)과 DCOM 옵션

CIM 명령어는 지정이 없으면 로컬 WMI에 COM으로 접속하고, -ComputerName을 지정하면 WSMan(WinRM) 프로토콜로 임시 세션을 만들어 접속합니다. 동일한 컴퓨터에 여러 조작을 수행하는 경우에는 CIM 세션을 만들어 재사용하는 쪽이 성능상 유리합니다.3

# 단발성이라면 -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

WinRM을 구성할 수 없는 오래된 머신 등, WSMan으로 접속할 수 없는 상대에게는 DCOM 프로토콜을 선택할 수 있습니다.4

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

원격 조회의 전제는 다음과 같습니다.

  • 접속 대상에서 WinRM이 구성되어 있을 것. winrm quickconfig가 서비스의 자동 시작화・HTTP 리스너(기본 포트 5985)의 생성・방화벽 예외 등록을 일괄로 수행합니다.10 HTTPS(기본 포트 5986)로 접속하고 싶은 경우에는 이것만으로는 부족하며, 서버 인증서를 준비한 뒤 winrm quickconfig -transport:https 등으로 HTTPS 리스너를 별도로 구성해야 합니다.10
  • 경로상의 방화벽에서 해당 포트가 열려 있을 것. 인바운드 규칙 설계와 등록의 실무는 「Windows 방화벽과 업무 앱」 글에서 다룬 바와 같습니다.
  • 인증. 도메인 환경이라면 Kerberos로 상호 인증됩니다. 워크그룹에서는 Kerberos를 사용할 수 없기 때문에 클라이언트 측 TrustedHosts에 등록이 필요한 경우가 있습니다. 등록 대상은 필요 최소한으로 좁힙니다.10
  • 권한. 기본 구성에서는 원격 WMI 조회・조작은 접속 대상의 관리자 그룹에 속한 계정으로 수행하는 것이 기본입니다. 일반 사용자에게 개방하는 경우에는 WinRM과 WMI 네임스페이스 양쪽에서 액세스 권한 구성이 필요합니다.10
  • 또한 DCOM은 고정된 대기 포트를 가지지 않기(RPC의 동적 포트를 사용) 때문에 방화벽을 넘나드는 설계가 어려워집니다. 앞으로 만들 구조는 WSMan을 기본으로 생각하는 것이 무난합니다.

5. C#에서의 이용 ── System.Management와 Microsoft.Management.Infrastructure

C#에서 WMI를 사용하는 API는 두 계통이 있습니다. 둘 다 Windows 전용입니다.

  System.Management Microsoft.Management.Infrastructure (MI API)
도입 .NET Framework에서는 표준. 현행 .NET에서는 NuGet 패키지 System.Management5 NuGet 패키지 Microsoft.Management.Infrastructure6
진입점 클래스 ManagementObjectSearcher(WQL을 넘겨 조회)5 CimSession(Create → QueryInstances / InvokeMethod / Subscribe)6
타입 체계 ManagementObject / ManagementEventWatcher11 CimInstance / CimSession ── CIM 명령어와 동일한 타입3
원격 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이 발생하는 것이 첫 번째 정석적인 실수입니다.

5.2. MI API: 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");
}

PowerShell의 CIM 명령어가 반환하는 것과 동일한 CimInstance를 다루기 때문에 「PowerShell로 시험해 본 뒤 C#으로 옮겨 적는다」는 개발 흐름이 자연스럽게 이어집니다. C#과 PowerShell의 연계 자체를 설계한다면 「C#에서 PowerShell을 실행하고 결과를 객체로 받는 방법」도 참조하시기 바랍니다.

6. 자주 쓰는 실전 레시피

6.1. 정석 클래스 조견표

얻고 싶은 정보 클래스 주요 속성
제조사・기종명 Win32_ComputerSystem Manufacturer, Model
본체 시리얼 번호 Win32_BIOS SerialNumber
OS 버전・부팅 시각 Win32_OperatingSystem Caption, Version, LastBootUpTime
디스크 여유 용량 Win32_LogicalDisk DeviceID, FreeSpace, Size, DriveType9
서비스 상태 Win32_Service Name, State, StartMode
프로세스 목록 Win32_Process Name, ProcessId, CommandLine

6.2. 자산 관리의 정석: 시리얼 번호・모델명・디스크 여유 공간

# 기종 정보와 시리얼 번호(PC 자산 대장 대조용)
$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 세션 경유로 각 서버에 돌리기만 해도 에이전트 없는 디스크 감시의 기반이 완성됩니다.

6.3. 프로세스 시작 감지 ── 이벤트 구독

「폴링으로 Win32_Process를 정기적으로 취득해 차분을 보는」 것이 아니라 이벤트 구독으로 합니다. 프로세스 시작은 Win32_ProcessStartTrace(커널 트레이스 프로바이더의 이벤트 클래스. ProcessName / ProcessID / ParentProcessID 등을 가짐8)를 구독하는 것이 간단합니다.

# 관리자 권한의 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

또 하나의 방법이 임의의 클래스에 사용할 수 있는 범용 인스턴스 생성 이벤트(__InstanceCreationEvent)입니다. 이쪽은 WMI가 WITHIN으로 지정한 간격으로 폴링해 차분을 이벤트화하는 구조이므로, 감지 간격과 부하의 트레이드오프를 직접 정해야 합니다.

# 5초 간격 폴링으로 Win32_Process의 신규 인스턴스를 감시
$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를 잊지 말 것

상주 감시에 도입하는 경우에는 구독이 끊겼을 때의 재등록(서비스 재시작 시・오류 시)까지 설계에 포함하십시오. 디바이스 감시를 포함한 「상태 확인과 표시」의 설계론은 「외부 기기의 상태 확인과 표시의 베스트 프랙티스」에서 다루고 있습니다.

7. 함정 ── 성능・권한・64bit・저장소・날짜

7.1. SELECT *와 폴링 남용

WMI 조회는 「프로바이더가 그 자리에서 값을 만드는」 처리이며 공짜가 아닙니다. 정석적인 안티패턴은 두 가지입니다.

  • SELECT *의 습관적 사용. Win32_Process를 모든 속성으로 전건 취득하면 그만큼 프로바이더의 작업과 네트워크 전송(원격 시)이 늘어납니다. -Filter로 행을, -Property로 열을 좁히고, 후속 조작을 위해 키만 필요하다면 -KeyOnly를 사용합니다. 어느 쪽이든 「객체 크기와 네트워크 트래픽을 줄이기 위해」 공식적으로 마련된 수단입니다.3
  • 짧은 주기 폴링. 「1초마다 Get-CimInstance Win32_Process」와 같은 설계는 6.3절의 이벤트 구독으로 대체합니다. 아무래도 폴링형(WITHIN)을 사용해야 하는 경우에도 요구 사항에 대해 필요 충분한 간격까지 늘립니다.

또한 원격에 대해 한 대씩 -ComputerName으로 반복하는 것도 낭비가 큰 형태입니다. 임시 세션 생성이 조회할 때마다 실행되므로, 여러 조작은 CIM 세션 재사용으로 바꿉니다.3

7.2. 이벤트 구독의 권한

6.3절에서 설명했듯이 Win32_ProcessStartTrace 계열의 구독은 관리자 권한이 전제입니다.7 「개발기(관리자로 실행)에서는 동작했는데, 고객사의 일반 사용자 환경에서 감시가 동작하지 않는다」는 사고는 방화벽 알림 대화상자와 나란히 정석적인 사례입니다. 일반 사용자로 동작하는 업무 앱에 감시를 도입한다면, 감시 부분을 Windows 서비스(LocalSystem 등)로 분리하고 앱 본체와는 프로세스 간 통신으로 연결하는 구성을 검토하십시오.

7.3. 32bit/64bit와 프로바이더

64bit Windows에서는 프로바이더에 32bit판과 64bit판이 공존하는 것이 있으며, 기본값으로는 호출한 앱의 비트 수에 일치하는 쪽이 응답합니다.12 대표적인 예가 root\default의 레지스트리 프로바이더(StdRegProv)로, 32bit 앱에서 읽으면 Wow6432Node 쪽(32bit 뷰)의 값이 반환됩니다.12 「WMI로 읽은 레지스트리 값이 regedit로 본 값과 다르다」면 먼저 이것을 의심하십시오. 반대쪽 뷰가 필요한 경우에는 접속 시 컨텍스트에 __ProviderArchitecture(그리고 필수로 하려면 __RequiredArchitecture)를 지정해 명시적으로 요청할 수 있습니다.12 비트 수 문제의 전체상은 「C#에서 Win32 API를 안전하게 호출하기 ── P/Invoke 실무 가이드」에서도 다루고 있습니다.

7.4. WMI 저장소 손상 시의 증상과 대처

WMI의 클래스 정의는 저장소(단일 파일이 아니라 Repository 폴더 안의 파일 집합이 데이터베이스로 기능함13)에 저장되어 있습니다. 이것이 불일치를 일으키면 「존재해야 할 클래스를 찾을 수 없다」「네임스페이스가 유효하지 않다」와 같은 오류가, 앱 쪽은 아무것도 바꾸지 않았는데도 나타나기 시작합니다. 원인 분리와 복구에는 winmgmt.exe를 사용합니다.13

rem 정합성 검사(결과가 inconsistent라면 불일치 있음)
winmgmt /verifyrepository

rem 정합성 검사 + 불일치가 있으면 재구축(읽을 수 있는 내용은 병합됨)
winmgmt /salvagerepository

주의해야 할 것은 저장소 삭제나 초기화를 첫 번째 수단으로 삼지 않는 것입니다. WMI를 경유해 나오는 오류는 OS의 다른 부분에 기인하는 경우도 있으며, Microsoft는 저장소 삭제를 첫 번째 대처로 삼으면 「시스템이나 설치된 앱에 손상을 초래할 수 있다」고 명시하고 있습니다.13 /verifyrepository로 확인 → /salvagerepository로 복구, 순서를 지킵니다.

7.5. DMTF 날짜 형식의 변환

WMI의 날짜는 CIM 사양의 DMTF 형식 yyyymmddHHMMSS.mmmmmm±UUU(끝부분은 UTC로부터의 오프셋 분. 예: 20260801100000.000000+540) 문자열로 저장되어 있습니다. 원본 값을 문자열 처리로 잘라 붙이는 것은 그만두고 변환 API를 사용합니다.

  • C# (System.Management): ManagementDateTimeConverter가 DMTF 형식과 DateTime / TimeSpan의 상호 변환을 제공합니다.11
  • CIM 계열 API(Get-CimInstance / MI API): 날짜 속성이 DateTime으로 변환된 상태로 반환되므로, 애초에 이 문제를 마주치지 않습니다. (Get-CimInstance Win32_OperatingSystem).LastBootUpTime은 그대로 DateTime으로 계산에 사용할 수 있습니다.

8. WMI를 사용해서는 안 되는 상황 ── 수단의 판단표

WMI는 「통일된 읽기 창구」로서는 우수하지만 항상 최적의 해답은 아닙니다. 실무에서의 구분 사용 기준입니다.

하고 싶은 일 적합한 수단 WMI를 선택하지 않는 이유
하드웨어 정보・OS 구성 취득, 에이전트 없는 원격 조회 WMI/CIM 여기가 WMI의 본령. 전용 API를 개별로 두드리는 것보다 통일적
자체 앱 설정의 읽기・쓰기 레지스트리 직접 읽기(Microsoft.Win32.Registry)・구성 파일 WMI 경유의 레지스트리 조작은 우회로이며, 7.3절의 비트 수 문제도 떠안게 됨
CPU 사용률 등 고빈도・연속적인 성능 감시 성능 카운터(System.Diagnostics.PerformanceCounter 등) 카운터는 그것을 위한 구조. WMI의 짧은 주기 폴링은 부하와 정밀도에서 불리
단발성 OS 기능 호출・저지연이 필요한 처리 Win32 API(P/Invoke) WMI는 COM/프로바이더를 경유하는 만큼의 오버헤드가 있음
자체 프로세스 권한으로 충분한 로컬 프로세스 열거・조작 System.Diagnostics.Process 표준 라이브러리로 완결되어 의존성이 줄어듦
방화벽・네트워크 등 Windows 관리 기능의 구성 Get-NetFirewallRule전용 CIM 기반 명령어 원본 WMI 클래스를 찾는 것보다 용도별로 정비된 명령어군이 정확하고 안전
파일・폴더의 변경 감지 FileSystemWatcher 전용 API가 있는 영역에 WMI를 끌어들이지 않음

판단 기준은 단순합니다. 「전용 구조가 있는 영역에서는 전용 구조를 사용하고, 횡단적인 조회・원격 조회에는 WMI/CIM을 사용한다」입니다. 마지막 행의 Get-NetFirewallRule 등은 내부적으로는 CIM 위에 구축된 명령어군이며, 「WMI/CIM을 직접 건드리지 않고 그 혜택만 받는」 형태라고 할 수 있습니다.

9. 정리

  • CIM은 DMTF의 업계 표준, WMI는 그 Microsoft 구현체입니다. PowerShell의 CIM 명령어도 C#의 MI API도 이 표준을 따르는 현행 세대의 진입점입니다.
  • PowerShell에서는 Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent가 현행입니다. Get-WmiObject 등의 WMI 명령어는 PowerShell 7에 존재하지 않으므로, 새로운 스크립트는 5.1용이라도 CIM 쪽으로 작성합니다.
  • 원격 조회는 WSMan(WinRM)이 기본이며, 여러 조작은 CIM 세션을 재사용합니다. WinRM 미구성 상대에게는 DCOM 옵션이라는 대안이 있습니다.
  • C#은 System.Management(간편・로컬 지향)와 Microsoft.Management.Infrastructure(원격・감시 지향, CIM 명령어와 동일한 타입 체계) 두 계통 중에서 선택합니다. 둘 다 Windows 전용 NuGet 패키지입니다.
  • 프로세스 감시는 폴링이 아니라 이벤트 구독으로. Win32_ProcessStartTrace의 구독에는 관리자 권한이 필요합니다.
  • SELECT *와 짧은 주기 폴링을 피하고 -Filter / -Property / -KeyOnly로 좁힙니다. 32bit 프로세스에서의 조회는 32bit 프로바이더를 향한다는 점, DMTF 날짜는 변환 API를 사용한다는 점, 저장소 손상은 삭제가 아니라 verify→salvage 순서로 대처한다는 점을 기억해 두십시오.
  • 전용 구조가 있는 영역(설정・성능 카운터・단발성 API 호출)에는 WMI를 끌어들이지 않고, 횡단적인 조회와 원격 조회에 WMI/CIM을 사용한다 ── 이것이 사용처를 정하는 한 문장입니다.

관련 글

관련 상담 영역

합동회사 코무라소프트는 WMI/CIM을 사용한 하드웨어 정보 취득・프로세스 감시・원격 PC 조회의 업무 앱 도입, Get-WmiObject 기반 사내 스크립트의 CIM 명령어로의 이전, 「개발기에서는 동작하는데 고객사에서는 권한 오류가 나는」 종류의 원인 조사를 다루고 있습니다. PowerShell을 통한 시제작부터 C#으로의 본 구현까지 하나의 흐름으로 상담하실 수 있습니다.

참고 링크

  1. Microsoft Learn, About WMI. WMI가 WBEM(기업 환경의 관리 정보 접근을 위한 표준 기술을 개발하는 업계 이니셔티브)의 Microsoft 구현체라는 점, 관리 대상 표현에 CIM(Common Information Model) 업계 표준을 사용하며 CIM은 DMTF(Distributed Management Task Force)가 개발・유지한다는 점, 차세대판 MI(Windows Management Infrastructure)가 기존 WMI와 완전 호환된다는 점, 원격 WMI 접속이 DCOM으로 이루어지며 대안으로 WS-Management 기반 WinRM이 있다는 점에 대해.  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라는 점, Invoke-CimMethod와 조합한 GetOwner 호출 예시, Windows 전용 명령어라는 점에 대해.  2 3 4 5 6 7 8 9 10

  4. Microsoft Learn, New-CimSessionOption (CimCmdlets). CIM 세션 옵션에 WsMan용과 DCOM용 2개의 매개변수 세트가 있다는 점, -Protocol에 Dcom / Default / Wsman을 지정할 수 있다는 점, New-CimSessionOption -Protocol Dcom으로 작성한 옵션을 New-CimSession의 -SessionOption에 전달해 DCOM CIM 세션을 만드는 예시, DCOM 세션의 기본 위장 수준이 Impersonate라는 점에 대해.  2

  5. Microsoft Learn, ManagementObjectSearcher Class (System.Management). 지정한 WQL 쿼리를 기반으로 관리 객체 컬렉션을 취득하는, 관리 정보 취득의 가장 일반적인 진입점 클래스라는 점, ObjectQuery와 ManagementScope(WMI 네임스페이스)를 받아 Get()으로 ManagementObjectCollection을 반환한다는 점, 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). 클래스명 또는 쿼리식으로 인디케이션(이벤트)을 구독하고 -SourceIdentifier로 구독 이름을 붙인다는 점, Win32_ProcessStartTrace의 구독 예시와 그 실행에 PowerShell 관리자 실행이 필요하다는 주석, -Action 스크립트 블록 안에서 $Event.SourceEventArgs.NewEvent로부터 ProcessName / ProcessId를 참조하는 예시, -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가 키라는 점, DriveType = 3으로 좁히는 VBScript / C# 쿼리 예시에 대해.  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 일시・시간 간격과 CLR의 DateTime / TimeSpan의 상호 변환 메서드를 제공한다는 점에 대해.  2 3

  12. Microsoft Learn, Requesting WMI Data on a 64-bit Platform. 프로바이더에 32bit판과 64bit판이 공존하는 경우, 기본값으로는 32bit 앱(스크립트 포함)에는 32bit 프로바이더가, 64bit 앱에는 64bit 프로바이더가 응답한다는 점, 컨텍스트의 __ProviderArchitecture(32 또는 64)와 __RequiredArchitecture로 기본값이 아닌 쪽의 프로바이더를 요청・강제할 수 있다는 점(강제 시 해당 판이 없으면 WBEM_E_PROVIDER_LOAD_FAILURE), 레지스트리 프로바이더의 예시에서 32bit 클라이언트가 HKLM\SOFTWARE\Wow6432Node 쪽 데이터를 받는다는 점에 대해.  2 3

  13. Microsoft Learn, winmgmt. winmgmt.exe의 /verifyrepository가 WMI 저장소의 정합성 검사를 수행한다는 점, /salvagerepository가 정합성 검사 후 불일치 검출 시 저장소를 재구축하고 읽을 수 있었던 내용을 병합한다는 점, /resetrepository가 OS 최초 설치 시 상태로 되돌린다는 점, 저장소가 Repository 폴더 안의 파일 집합으로 데이터베이스로서 기능한다는 점, WMI를 경유한 오류가 OS의 다른 부분에 기인하는 경우도 있으며 첫 번째 대처로서의 저장소 삭제는 시스템이나 설치된 앱의 손상으로 이어질 수 있으므로 해서는 안 된다는 점에 대해.  2 3

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

WMI와 CIM은 무엇이 다른가요?
CIM은 DMTF(Distributed Management Task Force)가 책정・유지하는 「시스템이나 디바이스 등 관리 대상을 표현하기 위한 업계 표준 모델」입니다. WMI는 그 표준을 사용하는 WBEM이라는 이니셔티브의 Microsoft 구현체로, Windows에 내장되어 있습니다. 즉 CIM이 사양, WMI가 Windows상의 구현체라는 관계입니다. PowerShell의 Get-CimInstance나 C#의 Microsoft.Management.Infrastructure가 「CIM」을 표방하는 것은 이 표준을 따르는 API이기 때문이며, 접속 대상은 동일한 WMI 기반입니다. 일상적인 개발에서는 「WMI의 클래스(Win32_* 등)를 CIM 계열 API로 조회한다」고 이해해 두면 실무상 문제없습니다.
Get-WmiObject는 이제 사용할 수 없나요?
Windows PowerShell 5.1에서는 지금도 동작하지만, PowerShell 6 이후(현행 PowerShell 7)에서는 Get-WmiObject・Invoke-WmiMethod・Register-WmiEvent・Set-WmiInstance・Remove-WmiObject라는 WMI v1 명령어가 삭제되어 있어 실행할 수 없습니다. 동일한 기능은 CimCmdlets 모듈(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent 등)이 제공합니다. 새로 작성하는 스크립트는 5.1에서 돌릴 경우라도 CIM 명령어로 작성해 두는 것이 안전합니다. 그렇게 해 두면 PowerShell 7로 이전할 때 WMI 부분을 다시 작성할 필요가 없어집니다.
C#에서 WMI를 사용하려면 System.Management와 Microsoft.Management.Infrastructure 중 어느 쪽을 사용해야 하나요?
둘 다 Windows 전용이며, 현행 .NET에서는 NuGet 패키지로 이용합니다. System.Management는 ManagementObjectSearcher에 WQL을 넘기기만 하면 사용할 수 있는 고전적인 API로, 로컬 정보 취득이 중심이라면 이것으로 충분합니다. DMTF 날짜를 변환하는 ManagementDateTimeConverter도 여기에 포함됩니다. 한편 Microsoft.Management.Infrastructure(MI API)는 PowerShell의 CIM 명령어와 동일한 타입 체계(CimSession / CimInstance)를 가지며, WSMan을 경유한 원격 조회, 비동기 버전 메서드, 이벤트 구독(Subscribe)까지 일관되게 다룰 수 있습니다. 원격 PC 조회나 감시를 본격적으로 도입한다면 MI API를 선택하는 것이 합리적입니다.
원격 PC에 Get-CimInstance가 연결되지 않습니다. 무엇을 확인해야 하나요?
먼저 접속 대상에서 WinRM이 구성되어 있는지 확인하십시오. -ComputerName을 지정한 CIM 조작은 WSMan(WinRM) 프로토콜로 임시 세션을 만들기 때문에, 접속 대상에서 WinRM 서비스와 리스너가 동작하고 있는 것이 전제입니다. winrm quickconfig로 기본 구성(서비스 시작・리스너 생성・방화벽 예외)을 할 수 있습니다. 기본 포트는 HTTP가 5985, HTTPS가 5986이므로 경로상의 방화벽도 확인합니다. 워크그룹 환경에서는 Kerberos의 상호 인증을 사용할 수 없기 때문에 클라이언트 측 TrustedHosts에 등록이 필요한 경우가 있습니다. 아무래도 WinRM을 구성할 수 없는 상대에게는 New-CimSessionOption -Protocol Dcom으로 만든 옵션을 사용해 DCOM으로 접속하는 방법도 있습니다.
WMI의 날짜가 「20260801100000.000000+540」과 같은 형식으로 반환되는 이유는 무엇인가요?
WMI의 날짜는 DMTF의 CIM 사양에서 정한 문자열 형식(yyyymmddHHMMSS.mmmmmm±UUU, 끝부분은 UTC로부터의 오프셋 분)으로 저장되어 있기 때문입니다. 구형 Get-WmiObject나 System.Management에서 원본 값을 읽으면 이 문자열이 그대로 반환됩니다. C#(System.Management)에서는 ManagementDateTimeConverter에 DMTF 형식과 DateTime / TimeSpan의 상호 변환 메서드가 준비되어 있으므로, 직접 문자열을 잘라 붙이지 말고 이것을 사용하십시오. 또한 Get-CimInstance 등 CIM 계열 API로 취득한 경우에는 날짜 속성이 DateTime으로 변환된 상태로 반환되므로, 이 문제 자체를 마주치지 않습니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기