WMI/CIM을 C#·PowerShell에서 쓰기 ── 하드웨어 정보 가져오기·프로세스 모니터링·원격 조회의 실무 가이드
· 업데이트: · Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, 업무 앱, Windows 개발
수정 이력(4건, 최종 수정 2026년 08월 22일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- 지식 맵의 관계를 전면 점검해, 설명과 어긋나 있던 관계(방향 반전·과도한 일반화·술어 혼동)를 고쳤습니다. 본문 설명은 바꾸지 않았습니다.
- 지식 맵의 관계를 전면 점검해, 설명과 어긋나 있던 관계(방향 반전·과도한 일반화·술어 혼동)를 고쳤습니다. 본문 설명은 바꾸지 않았습니다.
- 지식 맵의 「이벤트 구독은 폴링에 권장된다」는 관계를 「이벤트 구독은 폴링을 대체한다」로 고쳤습니다. 본문 설명은 바꾸지 않았습니다.
- 구조 설명을 그림으로 따라갈 수 있도록 Mermaid 그림 19점을 추가했습니다. 업무 앱의 흔한 요구와 WMI/CIM의 대응, CIM 표준과 WMI 구현의 관계, WQL 조회가 네임스페이스에서 공급자로 이어지는 구조, CimInstance의 메서드 호출, 새 스크립트를 CIM cmdlet으로 쓰는 이유, 원격 연결 방식 선택과 WinRM 전제 구성, PowerShell 시제품에서 C#으로의 옮겨 적기, 속성 캐스트의 함정, 에이전트 없는 디스크 모니터링의 토대, 프로세스 시작 이벤트 구독의 흐름과 두 구독 방식의 대비, 상주 모니터링의 구독 재등록, 조회 범위 축소에 따른 성능 대책, 일반 사용자 환경의 모니터링 구성, 64bit 환경의 공급자 선택, 리포지토리 불일치 시 원인 분리 절차, DMTF 날짜 형식 처리, 수단 판단의 축을 그림으로 담았습니다. 본문 문장과 코드, 참고 링크는 바꾸지 않았습니다.
- 최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22175762)
아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.
Go Komura (2026). 「WMI/CIM을 C#·PowerShell에서 쓰기 ── 하드웨어 정보 가져오기·프로세스 모니터링·원격 조회의 실무 가이드」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/wmi-cim-practical-guide/
- DOI(등록된 아카이브)
- 10.5281/zenodo.22175762
- DOI(마지막 등록 버전)
- 10.5281/zenodo.22175763
「PC 시리얼 번호와 모델명을 업무 앱 화면에 띄우고 싶다」「서버 디스크 여유 공간을 모니터링해 경고하고 싶다」「특정 프로세스가 시작된 것을 감지하고 싶다」「떨어진 곳의 PC 상태를 한꺼번에 조회하고 싶다」── Windows 업무 앱이나 관리 도구를 만들 때 이런 요구는 흔합니다. 그리고 그 흔한 답이 WMI(Windows Management Instrumentation), 표준 이름으로 말하면 CIM(Common Information Model)입니다.
flowchart TB
accTitle: 흔한 요구와 WMI/CIM
accDescr: 시리얼 번호와 모델명 표시, 디스크 여유 공간 모니터링, 프로세스 시작 감지, 원격 PC 조회라는 업무 앱의 흔한 요구에 대한 흔한 답이 WMI이며, 표준 이름으로 말하면 CIM이다
r1["시리얼 번호·모델명"] --> ans["WMI(표준 이름은 CIM)"]
r2["디스크 여유 공간 모니터링"] --> ans
r3["프로세스 시작 감지"] --> ans
r4["원격 PC 조회"] --> ans
그림 1: 업무 앱에서 흔한 네 가지 요구에 대한 흔한 답이 WMI/CIM.
골치 아픈 점은 WMI 관련 정보가 옛것과 새것이 뒤섞여 있다는 것입니다. 검색하면 Get-WmiObject를 쓴 10년 전 글과 Get-CimInstance를 쓴 글이 같이 나오고, C# 쪽도 System.Management와 Microsoft.Management.Infrastructure 두 계통이 있습니다. 어느 쪽이 현재 작성법이고, 어느 쪽이 「지금도 동작하지만 새로 고르지 않는」 작성법인지 알기 어렵습니다. 실제로 Get-WmiObject는 PowerShell 7에 없어서, 5.1용으로 쓴 사내 스크립트를 옮길 때 갑자기 드러납니다.
이 글에서는 업무 앱에서 하드웨어 정보 가져오기·프로세스 모니터링·원격 PC 조회를 구현하는 C#/PowerShell 개발자를 대상으로, WMI/CIM 구조의 최소 이해부터 PowerShell CIM cmdlet, C#의 두 API, 자주 쓰는 실무 레시피, 성능·권한·64bit의 함정, 그리고 「WMI를 쓰지 말아야 할 장면」의 판단까지를 2026년 8월 시점의 1차 자료를 바탕으로 정리합니다.
1. 먼저 결론
- CIM은 DMTF가 제정하는 관리 정보의 업계 표준이고, WMI는 그 Microsoft 구현입니다. PowerShell이나 C#의 「CIM」 계열 API는 이 표준을 따른 현재 세대 API이며, 연결 대상은 같은 WMI 기반입니다.1
- PowerShell의 현재 선택은 CIM cmdlet(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent)입니다. 구 WMI cmdlet(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 cmdlet과 같은 형식 체계의 MI API가 맞습니다.56
- 프로세스 시작 감지는 이벤트 구독으로 하고, 폴링으로 하지 마십시오.
Win32_ProcessStartTrace구독은 관리자 권한으로 실행해야 합니다.78 SELECT *를 습관적으로 쓰지 마십시오.-Filter/-Property/-KeyOnly로 전송 데이터를 줄이는 것이 WMI 성능 문제의 절반을 막습니다.3- WMI는 만능이 아닙니다. 고빈도 성능 모니터링, 자체 앱 설정 읽기·쓰기, 단발 OS 기능 호출에는 성능 카운터·레지스트리·Win32 API·전용 cmdlet 쪽이 맞습니다(8장의 판단표).
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 26건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
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 |
flowchart TB
accTitle: CIM 표준과 WMI 구현의 관계
accDescr: DMTF가 제정·유지하는 CIM 표준을 WBEM 이니셔티브 틀에서 쓰고, 그 Microsoft 구현이 WMI이며, 다음 세대 MI는 기존 WMI와 완전 호환이고, CIM 계열 API의 연결 대상은 같은 WMI 기반이다
dmtf["DMTF가 제정·유지"] --> cim["CIM(업계 표준 모델)"]
wbem["WBEM(업계 이니셔티브)"] --> wmi["WMI(Microsoft 구현)"]
cim --> wmi
wmi -.-> mi["MI(다음 세대·완전 호환)"]
api["CIM 계열 API(PowerShell / C#)"] --> wmi
그림 2: CIM이 사양, WMI가 Windows상의 구현. CIM 계열 API의 연결 대상은 같은 WMI 기반.
개발자가 잡아야 할 구조는 다음 네 가지입니다.
- 네임스페이스(namespace): 클래스를 묶는 계층입니다. 일상 조회에서 쓰는 것은 거의 root/CIMV2이고, CIM cmdlet의 기본값도 여기입니다.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 cmdlet의 기본 조회 언어도 WQL입니다.3
「OS나 하드웨어 정보를 통일된 클래스와 쿼리 언어로 읽을 수 있다」── 이것이 WMI의 가치입니다. 거꾸로 말하면 쓰기나 조작은 Invoke-CimMethod로 호출할 수 있는 메서드를 가진 일부 클래스에 한정되며, 무엇이든 되는 구조는 아닙니다.
flowchart TB
accTitle: WMI 조회의 구조
accDescr: WQL 조회는 네임스페이스 root/CIMV2 안의 Win32_* 클래스를 향하고, 클래스 실체를 제공하는 공급자가 그 자리에서 OS에 물어 값을 만들어 결과가 돌아온다
wql["WQL로 조회"] --> ns["네임스페이스 root/CIMV2"]
ns --> cls["Win32_* 클래스"]
cls --> prov["공급자"]
prov --> osq["그 자리에서 OS에 질의"]
osq --> res["결과를 반환"]
그림 3: 조회는 네임스페이스, 클래스, 공급자 순으로 따라가며, 값은 그 자리에서 만들어집니다.
3. PowerShell에서 쓰기 ── CIM cmdlet이 현재 선택, WMI cmdlet은 삭제됨
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 객체이며, 날짜 속성(CreationDate나 LastBootUpTime 등)은 DateTime으로 변환된 채 돌아옵니다. 구 Get-WmiObject와 달리 가져온 객체는 메서드를 직접 갖지 않으므로, 메서드 호출은 Invoke-CimMethod에 넘깁니다.
flowchart TB
accTitle: CimInstance의 메서드 호출
accDescr: Get-CimInstance가 반환하는 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 cmdlet에서의 이전 대응표
PowerShell 6 이후(현재 PowerShell 7)에서는 다음 WMI v1 cmdlet이 삭제되어 있습니다. 같은 기능은 CimCmdlets 모듈(WMI v2)이 제공합니다.2
| 구(Windows PowerShell 5.1까지) | 현재(CIM cmdlet) | 보충 |
|---|---|---|
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 cmdlet을 쓸 수 있으므로, 새로 쓰는 것은 5.1에서 돌리더라도 CIM 쪽으로 작성하는 것이 이전 비용을 남기지 않는 방법입니다. 5.1과 7의 공존·이전 전체 그림은 「Windows PowerShell 5.1과 PowerShell 7의 차이」에 정리했습니다.
flowchart TB
accTitle: 새 스크립트를 CIM으로 쓰는 이유
accDescr: WMI cmdlet으로 쓴 스크립트는 5.1에서는 동작하지만 PowerShell 6 이후에서 삭제되어 이전 때 다시 써야 하고, CIM cmdlet은 5.1에서도 쓸 수 있어 새로 쓰는 것은 CIM 쪽으로 쓰면 이전 비용이 남지 않는다
new["새로 쓰는 스크립트"] --> q1{"어느 쪽으로 쓸 것인가?"}
q1 -->|WMI cmdlet| old["5.1에서는 동작"]
q1 -->|CIM cmdlet| cur["5.1에서도 사용 가능"]
old --> del["PowerShell 7에서 삭제됨"]
del --> rew["이전 때 다시 작성"]
cur --> norew["이전 비용이 남지 않음"]
그림 5: 새 스크립트는 CIM cmdlet으로 써 두면, PowerShell 7로 옮길 때 다시 쓸 필요가 없습니다.
4. 원격 조회 ── CIM 세션(WSMan 기본)과 DCOM 옵션
CIM cmdlet은 지정이 없으면 로컬 WMI에 COM으로 연결하고, -ComputerName을 지정하면 WSMan(WinRM) 프로토콜로 임시 세션을 만들어 연결합니다. 같은 컴퓨터에 여러 작업을 할 때는 CIM 세션을 만들어 재사용하는 쪽이 성능상 유리합니다.3
flowchart TB
accTitle: CIM 연결 방식의 선택
accDescr: 지정이 없으면 로컬 WMI에 COM 연결, ComputerName 지정은 WSMan 임시 세션이 조회마다 만들어지고, 같은 상대에 대한 여러 작업은 New-CimSession 재사용이 성능상 유리하며, WinRM 미구성 상대에는 DCOM 프로토콜 옵션이 있다
exec["CIM cmdlet 실행"] --> q1{"ComputerName 지정?"}
q1 -->|없음| local["로컬 WMI에 COM 연결"]
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
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을 기본으로 보는 편이 무난합니다.
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
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 cmdlet과 같은 형식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이 난다는 것이 첫 번째 흔한 실수입니다.
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. 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 cmdlet이 반환하는 것과 같은 CimInstance를 다루므로, 「PowerShell로 시험한 뒤 C#으로 옮겨 적는다」는 개발 흐름이 자연스럽게 이어집니다. C#과 PowerShell 연동 자체를 설계한다면 「C#에서 PowerShell을 실행해 객체로 받는 방법」도 참고하십시오.
flowchart TB
accTitle: PowerShell로 시험한 뒤 C#으로 옮겨 적는 흐름
accDescr: PowerShell CIM cmdlet과 C# MI API는 같은 CimInstance 형을 다루므로, PowerShell로 시제품 만든 뒤 C#으로 옮겨 적는 개발 흐름이 자연스럽게 이어진다
trial["PowerShell로 시제품"] --> gci["CIM cmdlet"]
impl["C#으로 본구현"] --> mi["MI API"]
gci --> ci["같은 CimInstance 형"]
mi --> ci
ci -.-> flow["옮겨 적기가 자연스럽게 이어짐"]
그림 9: CIM cmdlet과 MI API는 같은 CimInstance 형을 다루므로, 시제품에서 본구현으로 이어집니다.
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 세션으로 각 서버에 돌리기만 해도, 에이전트 없는 디스크 모니터링의 토대가 됩니다.
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 / 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
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는 WMI가 WITHIN 지정 간격으로 폴링해 차이를 이벤트로 만들므로 감지 간격과 부하의 트레이드오프를 스스로 정한다
goal["프로세스 시작 감지"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["커널 추적 구독"]
t2 -.-> w1["WITHIN 간격으로 폴링"]
w1 -.-> tr["간격과 부하의 트레이드오프"]
그림 12: 전용 이벤트 클래스를 구독할지, 범용 인스턴스 생성 이벤트를 폴링 간격과 함께 쓸지입니다.
# 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를 잊지 말 것
상주 모니터링에 넣을 때는 구독이 끊겼을 때의 재등록(서비스 다시 시작 시·오류 시)까지 설계에 넣으십시오. 디바이스 모니터링을 포함한 「상태 확인과 표시」의 설계론은 「외부 기기의 상태 확인과 표시의 베스트 프랙티스」에서 다룹니다.
stateDiagram-v2
accTitle: 상주 모니터링의 구독 수명 주기
accDescr: 상주 모니터링에서는 구독 중인 상태가 서비스 다시 시작이나 오류로 끊길 수 있으므로, 끊긴 것을 감지해 재등록하고 구독 중으로 되돌리는 설계까지 넣는다
s1: 구독 중
s2: 구독이 끊긴 상태
s3: 재등록
[*] --> s1
s1 --> s2: 서비스 다시 시작·오류
s2 --> s3
s3 --> s1
그림 13: 상주 모니터링에서는 구독이 끊겼을 때 재등록해 구독 중으로 되돌리는 설계까지 넣습니다.
7. 함정 ── 성능·권한·64bit·리포지토리·날짜
7.1. SELECT *와 폴링 남용
WMI 조회는 「공급자가 그 자리에서 값을 만드는」 처리이며 공짜가 아닙니다. 흔한 안티패턴은 두 가지입니다.
SELECT *의 습관적 사용.Win32_Process를 모든 속성으로 전건 가져오면 그만큼 공급자의 일과 네트워크 전송(원격 시)이 커집니다.-Filter로 행을,-Property로 열을 좁히고, 후속 작업에 키만 필요하면-KeyOnly를 씁니다. 어느 쪽이든 「객체 크기와 네트워크 트래픽을 줄이기 위해」 공식으로 마련된 수단입니다.3- 짧은 주기 폴링. 「1초마다
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: 관리자 권한이 전제인 구독은 일반 사용자로 도는 앱 본체에서 분리하고, LocalSystem 등으로 실행하는 Windows 서비스에 모니터링 부분을 두어 앱 본체와는 프로세스 간 통신으로 잇는다
svcm["모니터링용 Windows 서비스"] --> subm["시작 추적을 구독"]
svcm -.-> lsm["LocalSystem 등으로 실행"]
appm["앱 본체(일반 사용자)"] ---|프로세스 간 통신| svcm
그림 15: 관리자 권한이 필요한 구독은 서비스 쪽으로 분리하고, 앱 본체와는 프로세스 간 통신으로 잇습니다.
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 실무 가이드」에서도 다룹니다.
flowchart TB
accTitle: 64bit 환경의 공급자 선택
accDescr: 기본값으로는 호출한 앱의 비트 수에 맞는 공급자가 응답하고, 32bit 앱의 레지스트리 조회는 Wow6432Node 쪽 값을 받지만, __ProviderArchitecture 지정으로 반대쪽 뷰를 명시적으로 요청할 수 있다
q1{"호출 측의 비트 수는?"} -->|32bit| p32["32bit 공급자가 응답"]
q1 -->|64bit| p64["64bit 공급자가 응답"]
p32 -.-> wow["레지스트리는 Wow6432Node 쪽 값"]
ctx["__ProviderArchitecture 지정"] -.-> ov["반대쪽 뷰를 명시적으로 요청"]
그림 16: 기본값으로는 호출 측 비트 수에 맞는 쪽이 응답하므로, 32bit 앱은 Wow6432Node 쪽을 읽습니다.
7.4. WMI 리포지토리 손상 시의 증상과 대처
WMI의 클래스 정의는 리포지토리(단일 파일이 아니라 Repository 폴더 안의 파일 집합이 데이터베이스로 동작합니다13)에 저장됩니다. 여기가 불일치하면 「있어야 할 클래스를 찾을 수 없다」「네임스페이스가 유효하지 않다」 같은 오류가, 앱 쪽은 아무것도 바꾸지 않았는데도 나타나기 시작합니다. 원인 분리와 복구에는 winmgmt.exe를 씁니다.13
rem 정합성 검사(결과가 inconsistent이면 불일치 있음)
winmgmt /verifyrepository
rem 정합성 검사 + 불일치가 있으면 재구축(읽을 수 있는 내용은 병합됨)
winmgmt /salvagerepository
주의할 점은 리포지토리 삭제나 초기화를 첫 수단으로 삼지 않는 것입니다. WMI를 거쳐 나오는 오류는 OS의 다른 부분에 원인이 있는 경우도 있고, 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["OS의 다른 부분의 원인을 의심"]
salvage -.-> merge["읽을 수 있는 내용은 병합됨"]
del["리포지토리 삭제·초기화"] -.-> ng["첫 수단으로 하지 않음"]
그림 17: verify로 확인한 뒤 salvage로 복구하는 순서를 지키고, 삭제를 첫 수단으로 하지 않습니다.
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으로 계산에 쓸 수 있습니다.
flowchart TB
accTitle: DMTF 날짜 형식의 처리
accDescr: WMI 날짜는 DMTF 형식 문자열로 저장되어 있으며, System.Management에서 원본 값을 읽은 경우에는 ManagementDateTimeConverter로 변환하고, CIM 계열 API라면 DateTime으로 변환된 채 돌아오므로, 직접 문자열을 잘라 붙이지 않는다
dmtf["DMTF 형식 문자열"] --> q1{"어느 API로 가져왔는가?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|CIM 계열 API| done["DateTime으로 변환된 채 반환"]
conv --> dtv["DateTime / TimeSpan으로 변환"]
cut["직접 문자열 잘라 붙이기"] -.-> ng["쓰지 않음"]
그림 18: DMTF 문자열 변환은 변환 API에 맡기고, CIM 계열 API라면 변환된 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 기반 cmdlet |
원본 WMI 클래스를 찾는 것보다 용도별로 갖춰진 cmdlet 군이 정확하고 안전 |
| 파일·폴더의 변경 감지 | FileSystemWatcher |
전용 API가 있는 영역에 WMI를 끌어들이지 않음 |
판단의 축은 단순합니다. 「전용 구조가 있는 영역에서는 전용 구조를 쓰고, 횡단 조회·원격 조회에는 WMI/CIM을 쓴다」입니다. 마지막 행의 Get-NetFirewallRule 등은 내부적으로는 CIM 위에 만든 cmdlet 군이며, 「WMI/CIM을 직접 건드리지 않고 그 혜택만 받는」 형태라고 할 수 있습니다.
flowchart TB
accTitle: 수단 판단의 축
accDescr: 전용 구조가 있는 영역에서는 전용 구조를 쓰고, 없는 영역의 횡단 조회나 원격 조회에 WMI와 CIM을 쓰는 것이 판단의 축이며, 전용 CIM 기반 cmdlet은 WMI와 CIM을 직접 건드리지 않고 혜택만 받는 형태이다
q1{"전용 구조가 있는가?"} -->|있다| ded["전용 구조를 쓴다"]
q1 -->|없다| wmi["WMI / CIM을 쓴다"]
wmi -.-> use["횡단 조회·원격 조회"]
cmd["전용 CIM 기반 cmdlet"] -.-> ben["혜택만 받는 형태"]
그림 19: 전용 구조가 있는 영역에서는 전용을 쓰고, 횡단 조회와 원격 조회에 WMI/CIM을 씁니다.
9. 정리
- CIM은 DMTF의 업계 표준, WMI는 그 Microsoft 구현입니다. PowerShell CIM cmdlet도 C# MI API도 이 표준을 따른 현재 세대의 진입점입니다.
- PowerShell에서는 Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent가 현재 선택입니다. Get-WmiObject 등 WMI cmdlet은 PowerShell 7에 없으므로, 새 스크립트는 5.1용이라도 CIM 쪽으로 씁니다.
- 원격 조회는 WSMan(WinRM)이 기본이며, 여러 작업은 CIM 세션을 재사용합니다. WinRM 미구성 상대에는 DCOM 옵션이라는 우회로가 있습니다.
- C#은 System.Management(간편·로컬 지향)와 Microsoft.Management.Infrastructure(원격·모니터링 지향, CIM cmdlet과 같은 형식 체계) 두 계통에서 고릅니다. 둘 다 Windows 전용 NuGet 패키지입니다.
- 프로세스 모니터링은 폴링이 아니라 이벤트 구독으로. Win32_ProcessStartTrace 구독에는 관리자 권한이 필요합니다.
- SELECT *와 짧은 주기 폴링을 피하고 -Filter / -Property / -KeyOnly로 좁힙니다. 32bit 프로세스에서의 조회는 32bit 공급자를 향한다는 점, DMTF 날짜는 변환 API를 쓴다는 점, 리포지토리 손상은 삭제가 아니라 verify→salvage 순서로 대처한다는 점을 기억해 두십시오.
- 전용 구조가 있는 영역(설정·성능 카운터·단발 API 호출)에는 WMI를 끌어들이지 않고, 횡단 조회와 원격 조회에 WMI/CIM을 쓴다 ── 이것이 사용처를 정하는 한 문장입니다.
관련 글
- C#(CSharp)에서 PowerShell을 실행해, 객체로 받는 방법
- PowerShell 실용 명령 모음 ── 일상 작업에서 자주 쓰는 작은 기능 늘리기
- Windows PowerShell 5.1과 PowerShell 7의 차이 ── 사내 스크립트 이전의 실무 가이드
- 외부 기기의 상태 확인과 표시의 베스트 프랙티스 - 「접속 중」만으로 끝내지 않는 설계
- C#에서 Win32 API를 안전하게 호출하기 ── P/Invoke 실무 가이드(DllImport / LibraryImport / CsWin32)
- Windows의 TPM이란 무엇인가 ── 그림으로 보는 「키를 밖으로 내보내지 않는 금고」와 측정 부팅
관련 상담 영역
합동회사 코무라소프트는 WMI/CIM을 쓴 하드웨어 정보 가져오기·프로세스 모니터링·원격 PC 조회의 업무 앱 도입, Get-WmiObject 기반 사내 스크립트의 CIM cmdlet으로의 이전, 「개발기에서는 되는데 고객사에서 권한 오류가 난다」 종류의 원인 조사를 다룹니다. PowerShell 시제품부터 C# 본구현까지 한 흐름으로 상담하실 수 있습니다.
참고 링크
-
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
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. WMI v1 cmdlet(Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject)이 PowerShell에서 삭제되었다는 점, CimCmdlets 모듈(WMI v2)의 cmdlet이 같은 기능을 제공하며 새 기능과 다시 설계된 구문을 갖는다는 점에 대해. ↩ ↩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라는 점, Invoke-CimMethod와 조합한 GetOwner 호출 예, Windows 전용 cmdlet이라는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). CIM 세션 옵션에 WsMan용과 DCOM용 두 매개변수 집합이 있다는 점, -Protocol에 Dcom / Default / Wsman을 지정할 수 있다는 점, New-CimSessionOption -Protocol Dcom으로 만든 옵션을 New-CimSession의 -SessionOption에 넘겨 DCOM CIM 세션을 만드는 예, DCOM 세션의 기본 가장 수준이 Impersonate라는 점에 대해. ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). 지정한 WQL 쿼리를 바탕으로 관리 객체 컬렉션을 가져오는, 관리 정보 조회의 가장 일반적인 진입점 클래스라는 점, ObjectQuery와 ManagementScope(WMI 네임스페이스)를 받아 Get()으로 ManagementObjectCollection을 반환한다는 점, 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 관리자 실행이 필요하다는 주석, -Action 스크립트 블록 안에서 $Event.SourceEventArgs.NewEvent로부터 ProcessName / ProcessId를 참조하는 예, -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가 키라는 점, DriveType = 3으로 좁히는 VBScript / C# 쿼리 예에 대해. ↩ ↩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 일시·시간 간격과 CLR의 DateTime / TimeSpan 상호 변환 메서드를 제공한다는 점에 대해. ↩ ↩2 ↩3
-
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
-
Microsoft Learn, winmgmt. winmgmt.exe의 /verifyrepository가 WMI 리포지토리 정합성 검사를 한다는 점, /salvagerepository가 정합성 검사 후 불일치 검출 시 리포지토리를 재구축하고 읽을 수 있었던 내용을 병합한다는 점, /resetrepository가 OS 최초 설치 시 상태로 되돌린다는 점, 리포지토리가 Repository 폴더 안의 파일 집합으로 데이터베이스로서 동작한다는 점, WMI를 거친 오류가 OS의 다른 부분에 원인이 있는 경우도 있으며 첫 대처로서의 리포지토리 삭제는 시스템이나 설치된 앱의 손상으로 이어질 수 있으므로 해서는 안 된다는 점에 대해. ↩ ↩2 ↩3
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
Windows 앱 외주·수탁 개발을 의뢰하기 전에 정리할 점
Windows 앱 외주·수탁 개발을 의뢰하기 전에, 기존 소프트웨어 수정, 장치 연동, COM/ActiveX, 배포·업데이트, 유지보수를 정리하는 포인트를 설명합니다.
인수는 왜 깨지는가 ── Windows 명령줄 인수의 규칙
Windows에는 인수의 배열이 존재하지 않으며, CreateProcess에 전달되는 것은 한 줄의 문자열이고 분할은 받는 쪽이 합니다. CommandLineToArgvW·CRT·.NET의 분할 규칙과 .NET의 ArgumentList, C++에...
멀티스레드 실무 베스트 프랙티스 .NET 편 ── 스레드를 늘리기 전에 정해 둘 것
「스레드를 만들었더니 가끔 죽거나 멈춘다」를 막는 설계의 정석을 .NET/C# 대상으로 정리합니다. 스레드를 직접 만들지 않고 Task에 맡기기, 공유 가변 상태 줄이기, 락의 규율, CancellationToken으로 정지 설계하기, UI 스레...
업무 앱의 연호·공휴일·마감일 처리 ── 연호 변경에 강한 설계와 JapaneseCalendar·영업일 계산 실무
연호 표시, 공휴일을 제외한 영업일 계산, 마감일 처리처럼 일본 업무 앱에 특유한 날짜 처리를 정리합니다. JapaneseCalendar와 연호 변경에 강한 설계, 내각부 CSV로 공휴일 마스터를 운영하는 방법, AddMonths의 월말 조정 사...
Windows 앱의 다중 실행 방지 ── 이름 있는 Mutex와 이중 실행 시 활성화
업무용 Windows 앱의 다중 실행 방지를 이름 있는 Mutex로 구현하는 방법을 정리합니다. Global\와 Local\ 차이로 생기는 RDP 환경의 함정, AbandonedMutexException, 기존 인스턴스를 앞으로 가져오기까지 설명...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- 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 cmdlet이 삭제되어 실행할 수 없습니다. 같은 기능은 CimCmdlets 모듈(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent 등)이 제공합니다. 새로 쓰는 스크립트는 5.1에서 돌리더라도 CIM cmdlet으로 작성해 두는 편이 안전합니다. 그렇게 하면 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 cmdlet과 같은 형식 체계(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으로 변환된 채 돌아오므로, 이 문제 자체를 만나지 않습니다.