SimpleIsBest.NET

유경상의 닷넷 블로그
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
이 글은 월간 마이크로소프트웨어(일명 마소) 2004년 8월호 닷넷 칼럼에 기고한 글입니다. 어떤 분께서 필요하시다고 해서 아픈 몸을 이끌고 열심히 작업했습니다. 그래두 호좁한 제 글을 찾아주시는 분이 있어서 기쁠 따름입니다. 아무쪼록 도움이 되시길 바랍니다. 무쟈게 깁니다. 알아서 보십시요.... -_-

주) 이 글은 마소 편집부에 제가 발송한 원고를 약간 편집하여 올린 글입니다(마소에는 실릴 수 없는 유행어나 필자 주석 등이 추가됨). 따라서 마소에 실린 글과는 약간의 차이가 있을 수 있습니다.


Management & Instrumentation of Your own Application

Subtitle : Windows Management Instrumentation (WMI) in .NET

기술은 발전한다. 그리고 기술의 발전과 더불어 그 복잡도는 기하 급수적으로 늘어나기 마련이다. 소프트웨어의 발전 역사를 살펴보면 가희 전광석화 같은 발전 모습을 볼 수 있다. 하지만 눈부신 발전 뒤에 숨겨진 기술의 복잡도는 숨이 막힐 지경이다. 개발자들은 이러한 복잡한 기술 속에서 살고 있다. 예전엔 간단히 로컬 데이터베이스에 접속하는 기능만 있으면 충분했었지만 현재의 어플리케이션은 원격 어플리케이션 서버에 존재하는 미들웨어를 통해 데이터베이스에 접속하거나 SOA(Service Oriented Architecture)라는 미명(?)하에 웹 서비스 프로토콜을 이용하여 웹 서버를 통해야만 미들웨어에 접근할 수 잇는 경우도 많다. 기껏 서버라곤 1~2대로 충분했던 시절이 지금은 웹 서버만 100대가 넘는 경우도 어렵지 않게 찾아 볼 수 있다.

전체적인 시스템의 구조가 복잡해 짐에 따라 크게 대두되는 문제 중 하나는 이들 복잡한 시스템을 어떻게 관리할 것인가 이다. 기업이 어느 기술이나 시스템을 도입하는데 있어서 중요한 의사 결정 사항 중 하나는 관리의 편의성이다. 소위 다운 사이징(down-sizing)이라는 개념하에 값싼 서버를 다수 사용함으로써 대형 서버를 사용하는 것 보다 적은 비용으로 동등하거나 더 높은 성능을 낼 수 있는 것이 사실이다. 하지만 시간이 흘러 감에 따라 수 많은 서버를 관리해야 하는데 소요되는 비용 역시 무시할 수는 없는 것이다. 여러분이 기업 전산실에 근무하고 있고 관리해야 할 웹 서버가 50대에 이른다고 생각해보자. 그리고 여러분의 상사가 한마디를 툭 던진다.

“XX씨. 내일까지 각 서버에 어떤 운영체제가 설치되어 있고 서비스 팩(패치)는 어떤 것이 설치되어 있으며, 응용 프로그램들은 어떤 것들이 설치되어 있는지 보고서를 작성해줘요.”

쓰봉... 미칠 노릇이다. MOM(Microsoft Operation Manager)나 IBM의 티볼리(Tivoli)와 같은 관리 솔루션이 없다면 여러분은 야근 작업으로 보고서를 작성해야 할 것이며, 이와 같은 작업은 신종 바이러스가 출현하고 핫픽스(hot-fix)가 등장하면 서버들의 핫픽스 설치 여부를 확인하기 위해 반복될 수도 있다.

Windows Management Instrumentation (WMI)

앞서의 예처럼 반복적인 관리 이슈를 해결하기 위해 Microsoft는 윈도우 운영체제에 대한 관리를 위한 인프라를 제공한다. 이것이 Windows Management Instrumentation(WMI) 이며 WMI를 통해 개발자 혹은 관리자는 윈도우 시스템의 다양한 정보를 얻거나 시스템 관리에 필요한 행동을 취할 수 있다.

Getting Start

WMI로 할 수 있는 유용한 작업이 무엇인가를 보여주는 좋은 예를 살펴보자. 리스트 1은 시스템에 설치된 핫픽스의 목록을 나열하는 VBScript 코드이다. 이 코드는 WMI가 제공하는 모니커 (winmgmts)를 통해 Win32_QuickFixEngineering의 목록을 나열하는 것이다. 물론 Win32_QuickFixEnginerring이 의미하는 것이 시스템에 설치된 핫픽스 임에는 두말할 나위도 없다(Win32_QuickFixEnginerring은 WMI 클래스이다. WMI 클래스에 대한 내용은 후에 상세히 다루도록 하겠다). 리스트 2 역시 동일한 작업을 하는 C# 코드로서 WMI를 액세스하는 닷넷 예제 코드이다. 리스트 1 이나 리스트 2 와 같은 작업 뿐만 아니라 WMI를 통해 시스템에 다양한 정보를 취득할 수 있다. 예를 들어, 현재 시스템의 Win32 서비스들의 목록, 이들 중에서 현재 ‘작동중’인 서비스들의 목록, 혹은 시스템에 설치된 응용프로그램들과 같은 시스템 소프트웨어에 대한 정보들을 얻을 수 있으며, CPU 정보, 물리 메모리 양, 네트워크 디바이스 정보, 디스크에 대한 정보 등등 하드웨어 정보 역시 WMI를 통해 모두 얻을 수 있다. 이러한 정적인 시스템 정보 뿐만 아니라 WMI를 통해 시스템을 리부팅 한다든가, 특정 서비스를 시작하거나 중단하는 등의 작업을 수행할 수도 있다. WMI는 이처럼 윈도우 운영체제에 전반적인 관리 기능을 제공하는 중앙 집중적인 서비스를 제공한다.

Option Explicit
Dim 
objSWbemService, colSWbemObjectSet, objSWbemObject
Dim strComputer

strComputer 
"."
Set objSWbemService = GetObject("winmgmts:\\" & strComputer)
Set colSWbemObjectSet _
objSWbemService.InstancesOf(
"Win32_QuickFixEngineering")

For Each objSWbemObject in colSWbemObjectSet
If objSWBemObject.Description <> "" Then
WScript
.Echo objSWbemObject.Description
End If
Next

리스트 1. Listing System Hot-Fix (VBScript 버전)

using System;
using 
System.Management;

public class 
ListHotFixes
{
    
public static void Main()
    {
        ManagementClass cls 
=
                new 
ManagementClass(@"\\.\root\cimv2:Win32_QuickFixEngineering");
        
ManagementObjectCollection col cls.GetInstances();

        foreach
(ManagementObject obj in col) {
            
string desc (string)obj["Description"];
            if 
(desc !"")
                Console.WriteLine(desc)
;
        
}
    }
}

리스트 2. Listing System Hot-Fix (C# 버전)

WMI defined

WMI가 유용하다고 느껴지는가? 그렇다면 다른 사람이 WMI가 무엇이냐고 물었을 때 대답해 줄 수 있도록 구체적으로 WMI가 무엇인지 정의를 해보자. WMI는 윈도우 시스템과 일반 소프트웨어에 대한 관리와 계측(instrumentation)을 위한 단일 인터페이스를 제공하는 윈도우의 인프라이다. DMTF(Distributed Management Task Force)는 네트워크 상에서 분산되어 있는 다양한 시스템, 디바이스에 대한 관리 표준을 정하기 위해 WBEM(Web-Based Enterprise Management) 표준을 제정했고 이것을 마이크로소프트에서 구현한 것이 WMI 인 것이다. 말이 나왔으니 표준에 대해서 약간 더 이야기 하자면 DMTF는 WBEM 표준에서 다양하고 분산된 시스템 자원들에 대한 정보를 액세스하기 위해 단일 데이터 스키마를 표준으로 제정하고 이 스키마를 기준으로 자원 정보를 액세스 하도록 권고하고 있다. 이 데이터 스키마 표준은 CIM(Common Information Model)로서 WMI 역시 CIM 스키마를 따르고 있다.

용어상에서 혼동하지 말아야 할 것이 있다. 필자 역시 처음 WMI를 접했을 때 용어에서 오는 혼란스러움 이였는데, WBEM 이란 용어에 걸맞지 않게 CIM 이나 WMI는 웹과 별 관계가 없다. 웹 기반의 관리인 듯 하지만 실제로 웹을 통해 시스템을 관리하는 내용은 WMI와 거의 무관하다. 둘째로 DMFT에서 정의한 표준은 관리 대상이 되는 자원의 정보를 중앙 집중적으로 저장하는 방법에 대한 표준일 뿐이지 이 정보에 접근하는 액세스 표준은 정의하고 있지 않다. 예를 들어 윈도우에서 CIM을 구현한 WMI는 DCOM(Distributed-COM) 프로토콜과 COM 객체를 통해서 CIM Repository에 접근하는 반면, 선의 솔라리스에서는 자바 API를 사용하고 하위 네트워크 프로토콜 역시 윈도우의 그것과 매우 다르다는 점이다.

마지막으로 한가지 당부할 내용은 앞서 언급한 ‘데이터 스키마 표준’에서 스키마는 XML 스키마의 그것과 무관하다. XML 관점에서의 스키마라기 보다는 데이터베이스 관점에서의 스키마라고 생각하는 것이 더 적절할 것이다. 이제 곧 이 스키마의 의미에 대해서 상세히 논하게 될 것이니 아직까지는 이 정도로만 이해해 두기 바란다.

어찌 되었건 WMI의 핵심은 다양한 시스템 자원을 단일한 인터페이스와 일관된 데이터 스키마를 통해 관리하고자 하는 것이다. 좀 더 구체적으로 예를 들어 보자. 만약 여러분이 관리하는 서버의 물리 메모리의 양을 프로그램적으로 알고자 한다면 Win32 API (실제로 필자도 어떤 API를 호출해야 하는지 정확히 모른다. MSDN을 디비봐야...)를 생각할 것이다. 그리고 현재 시스템에 설치된 Hot Fix가 어떤 것인가를 알고자 한다면 레지스트리를 살펴볼 것이다. 이처럼 어떤 자원에 대한 정보를 얻거나 작업을 시도할 때, 그 자원에 종속적인 API나 프로토콜을 사용해야 하는데 이것이 매우 불합리하고 복잡하다는 것이다. 하지만 WMI는 WMI가 제공하는 데이터 스키마와 API를 통해 다양한 관리 작업을 단일하게 수행할 수 있도록 하기 위함이다. 멋지지 않은가? (아님 말고... -_-)

WMI Infrastructure

WMI는 WBEM 표준에 의거하여 하부 구조를 가지고 있다(그림 1). 가장 핵심적인 부분은 CIMOM(CIM Object Manager)로서 CIM 표준이 제시하는 데이터 스키마를 저장하는 CIM Repository를 관리하고 다양한 정보를 수집하며 클라이언트로부터의 요청을 서비스하는 역할을 수행한다. WMI에서 CIMOM은 winmgmts.exe라 불리는 프로세스에 의해서 서비스 된다. Windows 98/Me 등의 운영체제에서 이 프로세스는 일반 프로세스와 동일하게 수행되지만 Windows NT/2000/XP/2003 에서는 Win32 서비스로서 작동된다.

CIMOM은 운영체제, 하드웨어, 소프트웨어, 응용 프로그램 등 다양한 관리 대상으로부터 정보를 수집해야 하기 때문에 각 관리 대상에 대한 WMI 프로바이더(provider)와 통신하게 된다. 예를 들어 Win32 WMI 프로바이더는 윈도우 운영체제에 대한 정보와 하드웨어, 파일 시스템 등에 대한 전반적인 시스템 정보를 제공하는 한편 Event Log 프로바이더는 윈도우의 이벤트 로그로부터 다양한 정보를 CIMOM에게 제공한다. 비슷하게 다양한 프로바이더에서 다양한 정보들이 CIMOM으로 제공되며 CIMOM은 이들 정보를 WMI 클라이언트에게 제공하게 된다. 이들 프로바이더는 윈도우 운영체제에 기본적으로 포함되고 설치되는 것들이 있는가 하면, 추가로 CD에서 설치해야 하는 것들도 있다. 대표적인 예로 MSI 프로바이더는 Windows Installer에 의해 설치된 설치 패키지에 대한 정보를 제공하는 프로바이더이다. 이 프로바이더는 Windows 2000 이상에 포함되어 있지만 디폴트로 설치되지 않는다. 따라서 추가 설치가 필요한 프로바이더이다. 반면 SQL*Server가 제공하는 WMI 프로바이더도 존재한다. 이 프로바이더는 SQL*Server가 설치된 경우에만 사용이 가능하다.

예상 할 수 있듯이, 커스텀 프로바이더를 제작할 수도 있다. 프로바이더는 WMI에서 정의하는 일련의 COM 인터페이스를 구현하는 COM 객체일 따름이다. 따라서 여러분의 어플리케이션의 작동 상황을 보고하거나 현재 상태를 보고하는 메커니즘으로 WMI 프로바이더를 작성할 수도 있다. 닷넷 프레임워크는 이러한 커스텀 프로바이더를 이미 제공하고 있으므로 손쉽게 여러분의 어플리케이션 상태를 WMI를 통해 제공할 수도 있다. 상세한 내용은 잠시 후에 다루기로 하자.

WMI에서 제공하는 정보를 액세스하고자 하는 클라이언트는 CIMOM에 접근해야 한다. CIMOM에 접근하기 위해서 WMI는 일련의 API를 제공한다. 이 API는 C++를 위한 COM 인터페이스와 VBScript, VB 6.0, Delphi 등의 개발도구를 위한 COM 객체의 집합이다. 앞서 리스트 1은 Scripting API for WMI를 이용하여 WMI에 접근한 예제이다.

닷넷 프레임워크 역시 WMI를 위한 클래스 라이브러리를 제공한다. 이들 클래스는 System.Management 네임스페이스 하에 존재하며 이 네임스페이스는 System.Management.dll 어셈블리내에 정의되어 있다. System.Management 네임스페이스내의 클래스들은 WMI의 COM API를 닷넷 환경에서 사용이 용이하도록 래핑(wrapping)하고 있으며 추가적으로 WMI Provider 역할도 수행할 수 있도록 되어 있다.

그림 1. WMI Infrastructure

CIM structure

WBEM은 CIM 데이터 스키마를 표준으로서 정의한다고 언급했었다. CIM 데이터 스키마 표준에 대해서 좀 더 상세히 알아보도록 하자. CIM은 크게 CIM 클래스, 인스턴스, 네임스페이스로 구성되어 있다. CIM 클래스, 인스턴스, 네임스페이스에 대해 모두 상세히 언급하고자 하면 책 한 권이 나올 분량이므로 모두를 상세히 다루지 못하는 점을 양해 바란다. 관련 자료는 참고 문헌의 자료나 WMI에 관련된 서적을 참고하기 바란다.

CIM Class & Instance

CIM은 시스템의 여러 자원에 대한 정보를 표준 데이터 구조로서 제공할 수 있도록 정의된 표준이다. CIM의 구조는 객체 지향 데이터베이스와 매우 유사하다. 데이터 베이스는 테이블을 정의하고 테이블의 각 row는 테이블이 정의하는 데이터 엔티티에 대한 구체적인 인스턴스로 볼 수 있다. 마찬가지로 CIM은 제공하고자 하는 정보의 구조를 클래스로서 정의한다. 데이터베이스의 테이블이 CIM의 클래스에 대응된다고 생각하면 이해가 쉽겠다.

CIM 클래스는 속성(property)과 메쏘드(method) 그리고 한정자(qualifier)를 갖는다. 속성과 메쏘드는 쉽게 이해가 갈 것이다. 그렇지만 한정자는 무엇일까? 한정자는 닷넷의 특성(attribute)와 동등한 성격과 기능을 갖는다. 닷넷에서 닷넷 클래스에 대해 [serializable] 이나 [Transaction] 과 같은 특성을 부여하는 것과 마찬가지로 CIM 클래스에 대해 메타 데이터로서 다양한 한정자를 설정할 수 있다. 이러한 한정자는 WMI 인프라(CIMOM)와 프로바이더가 인식하는 한정자이거나 단순한 description과 같은 한정자가 존재하기도 한다. 한정자는 어떠한 프로바이더에 의해 클래스가 사용되는가에 따라 매우 상이하다(다르다... -_-). 따라서 클래스를 정의하는 프로바이더에 대한 문서를 살펴보아야만 한다. WMI에서 사용되는 대부분의 프로바이더에 대한 정보는 Platform SDK의 문서나 MSDN 문서를 참고하기 바란다(귀찮더라도 찾아봐라... 다 뼈가 되고 살이된다... 싫음 말고... -_-).

CIM 클래스의 속성은 클래스마다 고유의 속성이 정의될 수도 있지만 모든 클래스에 공통적으로 갖는 속성이 있다. 이들 속성을 시스템 속성이라 불리며 모든 클래스는 이 속성들을 갖는다. 이들 속성은 클래스의 경로(PATH), 네임스페이스, 슈퍼클래스 등을 기술하고 있다. 경로, 네임스페이스, 슈퍼 클래스 등에 대한 내용은 뒤에서 점차로 설명하기로 한다. 어찌 되었건 모든 클래스는 시스템 속성을 갖고 있으며 이들은 클래스 혹은 인스턴스의 메타 정보를 기술하는 용도로 사용된다는 점만 기억해 두자. 구체적인 시스템 속성들의 목록이 궁금하다면 WMI SDK 문서를 참고하기 바란다(또... 참고...).

데이터베이스의 테이블과는 다르게 CIM 클래스는 상속이 가능하다. 따라서 수퍼 클래스(CIM에서는 상위 클래스를 SUPERCLASS라는 용어를 사용한다)의 속성/메쏘드/한정자들을 파생 클래스는 상속받게 된다. CIM 클래스들은 인스턴스를 갖을 수 있는가 여부에 따라서 abstract 클래스와 concrete 클래스로 나뉜다. abstract 클래스는 클래스의 abstract 한정자가 존재하고 그 값이 true 이면 abstract 클래스가 되며 그렇지 않다면 concrete 클래스가 된다.

CIM 클래스에 대해 이 정도의 이해를 했다면 WBEM이 정의하는 CIM 데이터 스키마 표준이 무엇인가를 이해할 수 있다. WBEM은 다양한 시스템 자원 정보를 위한 CIM 클래스들을 표준으로 제공하고 있으며 WBEM에서 미리 정의된 클래스들이 CIM 표준 스키마가 되는 것이다.

실제로 WMI에는 CIM 표준 스키마로서 다양한 클래스들이 존재한다. 이들 클래스들은 모두 CIM_ 으로 시작하는 클래스 이름을 갖고 있다. 예들 들어 CIM_ManagedSystemElement, CIM_LogicalElement, CIM_PhysicalElement, CIM_Service, CIM_Thread 등 300 여개의 표준 클래스 들이 존재한다. 이들 클래스의 대부분은 abstract 클래스로서 제공된다. WBEM 표준을 따르는 관리 소프트웨어(WMI와 같은)는 이들 추상 클래스로부터 상속을 받아 구체적인 정보를 제공하는 클래스를 정의함으로써 표준을 준수하는 것이다. WMI 역시 이들 표준으로부터 상속을 받아 구체적인 윈도우의 정보를 제공하는데 CIM_Service는 WBEM의 표준이며, CIM_Service로부터 상속받은 WMI의 클래스는 Win32_BaseService, Win32_Service 클래스이다. CIM_Service 클래스는 abstract 클래스이므로 인스턴스가 존재하지 않으며, Win32_BaseService 는 WMI의 구현이지만 역시 abstract 클래스이므로 인스턴스가 존재하지 않는다. 반면 Win32_Service 클래스는 concrete 클래스로서 인스턴스가 존재한다.

리스트 2에서 사용한 Win32_QuickFixEngineering 클래스는 CIM_LogicalElement 클래스에서 파생된 클래스이며 abstract 한정자가 정의되어 있지 않다. 즉 concrete 클래스이다. 따라서 인스턴스가 존재하며 이 클래스의 인스턴스는 시스템에 설치되어 있는 Hot-fix들의 목록이 되는 것이다. 비슷하게 Win32_Service 클래스 역시 concrete 클래스이며 인스턴스가 존재하고, 이 인스턴스들은 시스템 내의 Win32 서비스들의 목록이 되는 것이다. Win32_Service 클래스의 조상 클래스인 CIM_Service 클래스는 서비스의 시작 여부를 가르키는 Started 속성(property)과 시작 유형을 나타내는 StartMode 등의 속성을 가지고 있다. 또 Win32_BaseService 클래스는 서비스가 ‘일시 중지’, ‘중지’를 지원하는가 여부를 나타내는 AcceptPause, AcceptStop 등의 속성을 갖는다. 따라서 Win32_Service 클래스의 인스턴스들은 Started, StartMode, AcceptPause, AcceptStop 등 속성을 모두 갖으며 Win32_Service 인스턴스에 대해 Started 속성을 검사하여 서비스가 수행중인가를 알아낼 수 있다.

또한 클래스의 인스턴스들은 서로를 구분할 수 있는 키 값을 가져야 한다. 키는 클래스의 속성들 중 한 개 이상이 키 프로퍼티로 설정되며, 한 클래스의 인스턴스들이 동일한 키를 가질 수 없다. 어느 속성이 키 임을 알리기 위해서는 속성에 대한 key 한정자(클래스, 속성, 메쏘드에 모두 한정자를 설정할 수 있다)의 값이 true 인가를 확인하면 된다.

WMI Namespace

WMI는 약 6000 여 개의 클래스를 제공하고 있다. 이렇게 많은 클래스들이 존재하기 때문에 클래스의 성격이나 WMI 프로바이더에 의해 클래스들을 분류하게 되는데 클래스 분류는 WMI 네임스페이스에 의해 구분된다. WMI 네임스페이스를 사용하는 이유는 클래스들을 논리적으로 서로 다른 공간에 배치함으로써 클래스를 찾거나 사용하기 용이하다는 점이다.

네임스페이스는 루트 네임스페이스로서 root 가 존재하고 그 하위에 다양한 네임스페이스들이 존재한다. 가장 핵심적인 네임스페이스는 root/cimv2로서 CIM 표준 클래스들(CIM_XXXX 클래스들)과 그들에 대한 WMI의 구현 클래스(Win32_XXXX)들이 존재한다. 그 외에 root/default 네임스페이스에는 레지스트리 변경 이벤트를 제공하는 클래스들이 존재한다. 또한 root/WMI 네임스페이스에는 CIM과 무관하게 WMI에서만 제공하는 윈도우 트레이스(trace) 정보 등에 관련된 클래스들이 존재한다.

이외에도 root 네임스페이스 하위에는 다양한 네임스페이스들이 존재하는데 이들 네임스페이스는 별도의 어플리케이션과 이 어플리케이션이 제공하는 WMI 프로바이더에 의해 제공되는 클래스들이 포함된다. 예를 들어 root\MSAPPS11은 Office 2003 과 함께 생성되는 네임스페이스로서 다양한 클래스들이 워드, 엑셀, 파워 포인트등에 대한 정보를 제공한다. 예를 들어 Win32_Word11Template 클래스는 현재 열려있는 워드 문서들에서 사용중인 워드 템플릿 파일(*.dot)의 목록을 반환한다.

네임스페이스는 손쉽게 생성할 수 있다. 네임스페이스를 생성하는 일반적인 네이밍 규칙CIM 표준으로부터 상속 받거나 윈도우 시스템 정보에 대한 클래스들은 일반적으로 root/cimv2 네임스페이스에 존재하는 것이 일반적이며 그 외에 특정 어플리케이션에 대한 정보를 제공하는 클래스는 root 밑에 어플리케이션을 구별할 수 있는 별도의 네임스페이스를 생성하는 것이다. 나중에 닷넷 상에서 네임스페이스를 직접 작성하고 클래스를 생성하는 예제를 다루어 볼 것이다.

System Classes

모든 네임스페이스에는 두 개의 __ (under score)로 시작하는 클래스들이 있다. __SystemClass, __Provider, __Namespace 등이 그 예인데, 이들 클래스는 모두 CIMOM 자체가 제공하는 클래스로서 CIM Repository 자체에 대한 정보를 제공한다. __SystemClass 클래스는 시스템 클래스들에 대한 추상 클래스 역할을 하며 __Provider는 CIM 표준 클래스로서 구체적인 클래스는 __Win32Provider 클래스이다. __Win32Provider 시스템 클래스는 네임스페이스 내에 등록된 WMI 프로바이더에 대한 정보를 제공한다. 비슷하게 __Namespace 클래스는 해당 네임스페이스 내에 존재하는 하위 네임스페이스들의 정보를 제공한다. 이처럼 시스템 클래스는 CIM Repository 자체에 대한 정보나 CIMOM 수준에서 제공되는 정보를 담는데 사용된다. 리스트 3은 root 네임스페이스의 하위 네임스페이스를 열거하는 예제 코드이다. 물론 네임스페이스는 트리 구조를 가지므로 WMI의 모든 네임 스페이스를 열거하고자 한다면 리스트 3의 코드를 약간 수정하여 재귀 호출(recursive call)을 통해 WMI의 모든 네임스페이스를 열거할 수 있을 것이다.

using System;
using 
System.Management;

public class 
EnumWmiNamespaces
{
    
public static void Main()
    {
        ManagementClass cls 
= new ManagementClass(@"\\.\root:__Namespace"null);
        
ManagementObjectCollection col cls.GetInstances();

        foreach
(ManagementObject obj in col) {
            
string desc (string)obj["Name"];
            
Console.WriteLine(desc);
        
}
    }
}

리스트 3. 네임스페이스를 열거하는 닷넷 예제 코드

Association Classes

WMI의 CIM Repository는 데이터베이스와 흡사하다. 하지만 관계형 데이터베이스가 갖는 특성인 JOIN 과 같은 관계 연산은 제공하지 않는다. 대신 유사한 관계 클래스(association class)를 제공한다. 관계 클래스는 두 클래스의 관계를 나타내는 클래스로서 관계형 데이터베이스에서 두 테이블의 관계를 기술하는 테이블과 유사하게 생각하면 된다. 관계 클래스는 클래스의 association 한정자의 값이 true인 클래스를 말한다.

구체적으로 예를 들어보자. 컴퓨터 시스템을 나타내는 Win32_ComputerSystem 클래스가 있다. 그리고 현재 수행중인 프로세스를 나타내는 Win32_Process 클래스가 있다. 그렇다면 특정 컴퓨터에서 수행중인 프로세스들은 어떻게 표현할까? 이러한 표현을 위해 Win32_SystemProcesses 관계 클래스가 존재하고 이 클래스는 Win32_ComputerSystem과 관련 있는 Win32_Process 를 나타낸다. 즉, Win32_SystemProcesses 클래스의 인스턴스는 특정 컴퓨터 시스템과 프로세스의 관계를 나타내며 그 관계의 의미는 ‘해당 컴퓨터에서 수행중인 프로세스’가 될 것이다.

관계 클래스는 상위 관리 객체로부터 하위 객체로 네비게이션(navigation)을 해 나갈 때 매우 유용하다. 만약 여러분이 Win32_ComputerSystem 클래스의 인스턴스를 갖고 있다면 관계 클래스의 인스턴스들을 이용해 해당 컴퓨터에서 수행중인 프로세스들, 디스크, 파티션 구조, 프로그램 그룹 등의 정보를 드릴 다운(Drill-down)해 나갈 수 있다.

WMI에는 다양한 관계 클래스들이 존재하며 이들에 대한 상세 목록은 WMI 프로바이더에 따라서 차이가 나므로 WMI SDK 문서를 참조하기 바란다(... 이젠...).

Event & Event Classes

WMI는 클래스와 인스턴스를 통해 정보를 제공하는 것 외에도 이벤트를 발생시킬 수 있다. 예를 들어, 특정 프로세스가 종료되면 어떤 행동을 취하는 WMI 클라이언트를 작성할 수 있는 것이다. WMI가 제공하는 이벤트는 인스턴스 이벤트와 커스텀 이벤트로 두 가지로 나누어 볼 수 있다.

인스턴스 이벤트는 인스턴스가 생성/삭제/변경됨을 알리는 이벤트이다. 매우 간단하게 보이지만 이 이벤트의 활용도는 무궁무진하다. 예를 들어 보자. 앞서 언급한 프로세스가 종료되었음을 알고자 한다면 어떻게 하면 될까? 윈도우의 프로세스를 나타내는 WMI 클래스(CIM 클래스)는 Win32_Process 클래스이다. 프로세스가 종료된다는 것은 Win32_Process 클래스의 인스턴스가 삭제됨을 의미하므로 Win32_Process 인스턴스의 삭제 이벤트를 받는다면 프로세스의 종료를 감지할 수 있을 것이다.

WMI는 인스턴스 이벤트로 __InstanceOperationEvent 시스템 클래스에서 파생된 4개의 이벤트 클래스를 정의하고 있다. __InstanceCreationEvent, __InstanceDeletionEvent, __InstanceModificationEvent, __MethodInvocationEvent 가 그것들 인데 클래스 이름만 보아도 어떤 용도인지 알 수 있을 것이다. WMI의 관점에서 보면 인스턴스 이벤트의 발생은 이들 인스턴스 이벤트 클래스의 인스턴스가 생성되는 것이다. 따라서 이벤트 발생을 감지하고자 한다면 이들 클래스의 인스턴스가 발생했나를 살펴보는 행위가 되며, 구체적으로 이벤트 발생을 감지하고자 하는 행동을 이벤트 가입(event subscription)이라 한다. 이들 인스턴스 이벤트 클래스는 TargetInstance 속성을 갖고 있어서 이벤트를 발생한 대상이 어떤 인스턴스(예를 들어 Win32_Process의 인스턴스)인가를 알아낼 수 있다. 특히, __InstanceModificationEvent 클래스는 PreviousInstance 속성도 가지고 있어서 인스턴스가 어떻게 변경되었는가도 알아 낼 수 있다.

리스트 4는 프로세스 종료를 감시하는 간단한 WMI 이벤트 가입 예제이다. 이 코드는 Win32_Process 클래스의 인스턴스가 삭제될 때(프로세스가 종료될 때이다) __InstanceDeletionEvent 클래스의 인스턴스가 생성되므로, 이를 매 1초마다 감시를 하는 것이다. 다시 말해 Win32_Process의 인스턴스가 삭제되는 것을 감시하는 코드이다. 리스트 4를 수행시키고 임의의 프로세스가 종료하면 1초 이내로 종료된 프로세스가 어떤 것인가를 콘솔에 출력할 것이다. 리스트 4에 등장하는 여러 닷넷 클래스에 대해서는 걱정하지 말자. 후에 WMI에 관련된 닷넷 클래스들을 다룰 것이다. 다만 리스트 4에서 인스턴스 이벤트를 어떻게 활용하는가에 주안점을 두고 살펴보자.

using System;
using 
System.Management;

public class 
MonitorProcessTerminate
{
    
public static void Main()
    {
        WqlEventQuery query 
= new WqlEventQuery("__InstanceDeletionEvent"
                                
new TimeSpan(001),      // every second
                                
"TargetInstance ISA 'Win32_Process'");
        
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
        
ManagementBaseObject objEvent watcher.WaitForNextEvent();
        
// getting Win32_Process instance
        
ManagementBaseObject objProcess 
                            
(ManagementBaseObject)objEvent["TargetInstance"];
        
Console.WriteLine("{0} Process terminate...", objProcess["Caption"]);
        
watcher.Stop();
    
}
}

리스트 4. 프로세스 종료를 감시하는 WMI 이벤트 예제

인스턴스 이벤트는 간단하지만 강력하다. 하지만 인스턴스 이벤트로는 해결하기 어려운 이벤트들이 존재한다. 레지스트리를 예를 들어 본다면, 특정 레지스트리의 키가 변경되었음을 이벤트로 알리고자 한다고 생각해 보자. 단순히 생각하면 레지스트리 키에 대한 인스턴스가 존재하고 그 인스턴스의 변경 이벤트를 받으면 될 것이다. 하지만 레지스트리의 키에 대한 WMI 클래스는 존재하지 않으며 따라서 키에 대한 인스턴스도 존재하지 않는다. 이처럼 WMI 인스턴스와 무관한 이벤트를 발생하고자 한다면 커스텀 이벤트를 사용해야 한다.

WMI 커스텀 이벤트는 __ExtrinsicEvent 시스템 클래스에서 파생된 클래스를 정의하면 된다. 레지스트리의 경우 __ExtrinsicEvent 클래스에서 파생된 RegistryEvent 클래스를 제공하며 다시 RegistryEvent에서 파생된 RegistryValueChangeEvent, RegistryKeyChangeEvent, RegistryTreeChangeEvent 클래스를 제공한다. 이들 클래스들은 레지스트리의 값, 키, 트리 및 하위 트리의 변경을 알리는 이벤트를 발생하는데 사용된다.

인스턴스 이벤트와 달리 커스텀 이벤트는 클래스의 프로퍼티를 통해 받고자 하는 이벤트를 결정하는데 사용되곤 한다. 레지스트리 이벤트의 경우, Hive 프로퍼티에 레지스트리 하이브를 설정하고 RootPath, KeyPath, ValueName 등의 프로퍼티에 조건을 설정하는 형태로 특정 레지스트리 트리, 키, 값이 변경되는 이벤트를 수신할 수 있다. 예제 코드는 지면관계상 이달의 디스켓 내의 Monitor Registry.cs 파일을 참고하기 바란다(필자주 : 이달의 디스켓은 마소 홈페이지에서 다운로드 할 수 있다. 귀차니즘의 엄습? 그래서 리스트를 걍 포함시켰다.).

/*
 * Monitor Registry.cs
 *  : monitoring registry chane using WMI
 *
 * Copyright (C) 2004 by Kyoung-Sang Yu
 *
 * Written by Loner
 *
 */
using System;
using 
System.Management;

public class 
MonitorRegistry
{
    
public static void Main()
    {
        WqlEventQuery query 
= new WqlEventQuery("RegistryTreeChangeEvent"
                                        
new TimeSpan(001),      // every second
                                        
"Hive='HKEY_LOCAL_MACHINE' AND RootPath='software'");
        
ManagementScope scope = new ManagementScope(@"\\.\root\default");
        
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
        
ManagementBaseObject objEvent watcher.WaitForNextEvent()// RegistryTreeChangeEvent instance
        
Console.WriteLine("{0}\\{1} tree and subtree (key/value) is changed...", objEvent["Hive"], objEvent["RootPath"]);
        
watcher.Stop();
    
}
}

Monitor Registry.cs 예제코드

WMI 기타 사항

WMI를 사용함에 있어서 추가적으로 알아두어야 할 사항들에 대해 몇 가지 기술한다. 비록 제목은 기타 사항이지만 WMI를 이해하는데 있어서 반드시 필요한 부분이므로 간과하지 말자.

WMI PATH

이벤트 클래스를 포함한 모든 WMI 클래스와 인스턴스는 PATH를 갖는다. 즉, 특정 클래스나 특정 인스턴스를 찾기 위해 PATH를 명시 할 수 있다는 것이다. WMI의 PATH는 ADSI(Active Directory Service Interface)에서 사용되는 PATH와 매우 흡사하다. WMI에서 사용하는 PATH는 다음과 같은 형태를 갖는다.

[\\ComputerName][\Namespace][:ClassName][.KeyProperty=’value’]

UNC Path와 흡사한 WMI PATH는 먼저 컴퓨터 이름과 네임스페이스가 명시되고 : (콜론) 이후에 클래스 명이 명시된다. 클래스에 대한 PATH라면 여기서 끝이 나겠지만 인스턴스에 대한 PATH라면 인스턴스를 구별할 키 속성이 경로에 명시되게 된다. 만약 컴퓨터 명 대신 . 이 사용되게 되면 로컬 컴퓨터를 의미한다. 다음은 WMI PATH에 대한 몇몇 예이다.

  • \\.\root\__NAMESPACE
  • \\Lancer\root\cimv2\Win32_Service
  • \\Svr1\root\cimv2\Win32_Service.Name=’Alerter’

첫 번째 예는 로컬 컴퓨터의 root 네임스페이스 내에 __NAMESPACE 클래스를 나타내는 WMI PATH이며 두 번째 예는 Lancer 컴퓨터의 root\cimv2 네임스페이스 내의 Win32_Service 클래스를 나타낸다. 세 번째는 Svr1 컴퓨터의 root\cimv2 네임스페이스 내의 Win32_Service 클래스 인스턴스 중 Name이 Alerter 인 인스턴스를 나타내는 예제이다.

모든 WMI 클래스 및 인스턴스는 __PATH 라는 시스템 속성을 가지고 있어서 이 속성 값을 통해 해당 클래스 혹은 인스턴스의 WMI PATH를 알아낼 수 있다. 또한 __RELPATH 속성을 통해 컴퓨터 이름과 네임스페이스를 제외한 상대 경로 역시 알아낼 수 있음을 알아 두자.

MOF

MOF(Management Object Format)은 WMI 클래스 및 인스턴스를 기술하기 위해 WBEM에서 제정한 디스크립션 언어이다. 즉, CIM 클래스(WMI 클래스) 및 인스턴스를 보다 쉽게 기술하기 위한 메타 언어로서 제정된 것이다. 다음은 CIM_Process 클래스에 대한 MOF의 일부이다.

[Abstract,UUID("{8502C566-5FBB-11D2-AAC1-006008C78BC7}") : ToInstance]
class CIM_Process : CIM_LogicalElement
{
[Key : ToInstance ToSubclass DisableOverride,Read : ToSubclass,MaxLen(256) : ToSubclass]
string Handle;
[Units("Milliseconds") : ToSubclass,read : ToSubclass]
uint64 KernelModeTime;
// 생략..
};

마치 C# 코드를 보는 것과 같이 클래스의 이름, 슈퍼 클래스 그리고 클래스의 한정자를 표시하고 또한 프로퍼티의 타입과 이름 그리고 프로퍼티의 한정자 역시 모두 기술되고 있음을 알 수 있다.

MOF의 용도는 보다 쉽게, 그리고 텍스트 파일로서 CIM 스키마를 명시할 수 있도록 하기 위함이다. ‘보다 쉽게’라는 말이 의미하듯이 MOF를 사용하지 않고도 클래스나 인스턴스를 기술할 수도 있다. WMI가 제공하는 COM API 및 스크립트 API는 코드를 통해 클래스를 생성하고 클래스의 한정자를 기술하며, 클래스가 갖는 프로퍼티, 메쏘드 등을 명세할 수도 있다. 하지만 이렇게 코드를 통해 클래스를 명세해야 한다면 속된말로 정말 빡센 일이 아닐 수 없다. 이 때문에 MOF가 등장했고 MOF 파일을 통해 클래스와 인스턴스를 기술하고 MOF 컴파일러(mofcomp.exe)를 통해 MOF를 컴파일하기만 하면 MOF 파일에 기술된 클래스와 인스턴스가 CIM Repository에 기록된다는 말이다. 편리하지 않은가? (아님 말고... 필자만 편리하다고 여기는감?)

실제로 CIM 표준 클래스와 WMI가 구현한 Win32 클래스들에 대한 MOF 파일이 궁금하다면 %SYSTEM_ROOT%\system32\webm\cimwin32.mof 파일을 한번 열어보는 것도 나쁘지 않다. MOF 스펙에 대한 상세한 설명은 WMI SDK 문서나 DMTF 홈페이지의 MOF 스펙을 참고하기 바란다(필자주: 이건 굳이 참고하지 않아도 된다. -_-).

WQL Query

앞서도 여러 번 언급했지만 WMI는 데이터베이스와 유사한 점이 많다. WQL(WMI Query Language)는 SQL과 비슷하게 WMI내의 클래스 및 인스턴스, 이벤트를 조회하기 위한 언어이다. WQL은 SQL과 매우 흡사하기 때문에 별도로 이것을 배우기 위해 노력하지 않아도 된다. 다만 WQL에서만 사용되는 몇 가지 연산자와 문장 예제만 ‘참고’해 두면 된다. 여기서는 WQL에 대해 상세히 설명하지 않고 몇 가지 WQL 예제만을 설명하도록 하겠다. WQL은 어렵지 않다. 필자를 믿어도 좋다.

  • SELECT * FROM Win32_Service
  • SELECT Handle, Caption FROM Win32_Process WHERE Caption = ‘w3wp.exe’
  • SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA Win32_Process

첫 번째 WQL은 Win32_Service 클래스의 모든 인스턴스를 반환하는 WQL 예제이다. SQL과 비교하여 전혀 다를 게 없다. 두 번째 WQL 예제는 Win32_Process 인스턴스들 중 Caption 속성이 w3wp.exe인 인스턴스를 반환하되 속성은 Handle과 Caption 속성만을 사용하겠다는 의미이다. 역시 SQL과 크게 다를 바가 없다. 세 번째 예제는 이벤트를 조회하는데 사용하는 쿼리로서 Win32_Process 클래스의 인스턴스 생성 이벤트가 발생하는 것을 매 5초 단위로 감시하라는 것이다.

이러한 WQL 쿼리를 통해 보다 효율적으로 원하는 정보만을 WMI 로부터 조회할 수 있다. 예를 들어 현재 상태가 ‘running’인 NT 서비스들을 알아 내거나 시작 모드가 ‘자동’인 NT 서비스들이 어떤 것들이 있는가를 알아내는 것은 매우 손쉬운 작업이 된다.

Dynamic vs. Static instance

WMI에서 제공되는 클래스의 인스턴스는 대상에 대한 정보를 포함하고 있다. 이들 정보는 수시로 변하는 동적(dynamic) 정보들과 변하지 않는 정적(static) 정보들이 있을 수 있다. 예를 들어 Win32_Process 클래스의 인스턴스는 동적인 정보들이다. 프로세스들은 수시로 시작되거나 종료되며 Win32_Process 클래스는 프로세스가 현재 얼마 동안 수행 중인가를 나타내는 속성들도 존재하기 때문이다. 반면 __Namespace 클래스의 인스턴스는 주어진 네임스페이스의 하위 네임스페이스들 나타낸다. 네임스페이스는 일단 생성되면 변경되는 부분이 없기 때문에 그 정보는 정적이라 할 수 있다.

Windows 2003 에는 6000 여개의 WMI 클래스들이 존재하고 정적인 정보를 나타내는 몇몇 시스템 클래스(__Namespace 같은)를 제외하곤 대다수는 동적인 정보를 나타낸다. 동적인 정보를 나타내는 클래스는 반드시 클래스에 dynamic 한정자가 정의되고 그 값이 true 이어야 한다. 그렇지 않은 클래스는 정적 클래스가 된다. 동적 클래스는 인스턴스에 대한 조회가 요청될 때, 즉 인스턴스가 생성될 때 인스턴스의 정보가 WMI 프로바이더에 의해 채워지게 된다. 따라서 동적 클래스는 반드시 WMI 프로바이더가 명시되어야 한다.

동적 클래스의 인스턴스인 동적 인스턴스는 해당 WMI 프로바이더가 수행 중 일 때 만 그 정보가 표시된다. 앞서 동적 클래스의 인스턴스는 생성시에 WMI 프로바이더가 그 값을 채워준다고 했었다. 따라서 WMI 프로바이더가 수행 중이 아니라면 동적 클래스의 인스턴스는 전혀 생성되지 않는다. 예를 들어, root\MSAPPS11:Win32_Word11Font 클래스는 Word 2003에서 ‘현재’ 사용중인 폰트들의 정보를 나열한다. 물론 이 클래스도 dynamic 한정자와 provider 한정자가 명시되어 있다. 만약 Word 가 수행 중이 아니라면 이 클래스의 인스턴스는 존재하지 않는 것 처럼 나타난다. Word를 수행한 후에 이 클래스의 인스턴스를 조회하면 폰트 정보들이 표시될 것이다.

동적 인스턴스는 CIM Repository에 저장되지 않는다. 클라이언트의 요구에 의해 on-demand로 생성되며 인스턴스가 수시로 변동하기 때문이다. 반면에 정적 인스턴스는 동적으로 변경이 이루어지지 않기 때문에 CIM Repository에 인스턴스 자체가 저장된다. 동적 인스턴스를 이해하는 것은 나중에 설명할 어플리케이션 Instrumentation에서 중요한 사항이므로 잘 이해하기 바란다.

WMI 관련 유틸리티 및 도구들

지금까지 거의 말로만 CIM 클래스니 프로퍼티니 한정자를 떠들어 왔다. 하지만 WMI 내에 구체적으로 어떤 클래스들이 있으며 그들의 인스턴스는 어떤 것인지 살펴볼 도구나 유틸리티들은 없는가? 반드시 프로그램을 작성해야 하는가? 여기서는 WMI에 관련된 몇 가지 유틸리티를 간단히 살펴보도록 하겠다.

WMI 테스터 & WMI Console

WMI 테스터(wbemtest.exe)Windows XP 이상에 기본적으로 포함된 WMI 테스트 도구이다. UI는 그다지 간편하지 못하지만 WMI 네임스페이스, 클래스, 프로퍼티, 인스턴스를 모두 조회하거나 생성할 수 있는 기능을 가지고 있다. 클래스의 속성을 검사하거나 WQL 조회를 수행하거나, 이벤트 subscription을 수행할 수 있다(화면 1).

화면 1. WMI 테스터 (wbemtest.exe)

WMI 콘솔은 커맨드 라인 상에서 WMI의 다양한 클래스 정보를 확인하고 인스턴스를 열거하는데 사용할 수 있다. 인터페이스나 커맨드가 다소 생소하지만 커맨드 라인상에서 작동하기 때문에 Telnet 이나 기타 방법을 이용하여 원격 제어가 가능하다는 장점이 있다.

화면 2. WMI 콘솔 (wmic.exe)

WMI Tools

WMI Tools는 WMI SDK에 포함되었던 WMI 관련 유틸리티를 별도로 다운로드 할 수 있도록 패키징 한 유틸리티 모음이다. WMI Tools에는 CIM Studio, CIM Object Browser 등의 유틸리티가 포함되어 있다. 이중 CIM Studio는 CIM repository에 존재하는 클래스들을 브라우징 하거나 클래스의 인스턴스를 조회하거나 추가/삭제하는 것이 가능하며 CIM 클래스의 정의를 변경할 수도 있다. 또한 클래스, 속성, 메쏘드의 한정자를 보거나 편집하는 것이 가능하며 특정 클래스와 그와 연관된 관계 클래스들이 어떤 것이 있는가를 그래픽으로 보여주기 때문에 매우 편리한 도구이다. 필자 역시 WMI에 관련된 작업을 할 때는 항상 CIM Studio로 작업하고 있다. CIM Object Browser는 CIM Studio와 비슷한 기능을 가지고 있다. CIM Studio가 클래스 관점에서 작동하는 유틸리티라면 CIM Object Browser는 인스턴스 관점에서 작동하는 유틸리티라고 보면 된다.

화면 3. CIM Studio

WMI SDK Reference

WMI를 잘 활용하기 위해서는 CIM 클래스(WMI 클래스)가 어떤 것들이 있으며 그것이 어떤 정보를 제공하는가를 잘 아는 것이 중요하다. 6000 여개의 클래스가 제공되지만 필요한 것을 찾지 못한다면 말짱 헛것이기 때문이다. 따라서 CIM Studio나 CIM Object Browser와 같은 도구를 통해 유용한 정보를 찾아 보도록하는 것이 중요하다.

이렇게 원하는 정보를 담는 WMI 클래스를 직접 찾아보는 것 보다, 일목요연하게 클래스들이 나열되있는 레퍼런스 역시 중요한 WMI의 도구라고 할 수 있다. 이런 의미에서 WIM 프로바이더 별로 클래스가 구분되어 설명되어 있는 WMI SDK의 레퍼런스는 중요한 참고 문헌이 될 것이다.

WMI in .NET Framework

지금까지 WMI에 대해 간략히(? !!) 살펴보았다. 실제로 WMI에 대한 기술은 내용이 상당히 방대한 편이므로 한정된 칼럼 지면에서 이들을 모두 설명하기는 어렵다. 하지만 지금까지 설명한 내용을 이해 한다면 이제부터 닷넷 프레임워크 상에서 WMI를 액세스하기 위한 코드 작성을 독자가 직접 해볼 수 있을 것이다. 리스트 2, 리스트 3, 리스트 4의 닷넷 코드에서 등장한 닷넷의 클래스들을 설명하도록 하겠다.

System.Management 네임스페이스

닷넷 환경에서 WMI에 접근하기 위해서는 System.Management 네임스페이스 하의 여러 클래스들을 사용하면 된다. 이들 클래스를 사용하기 위해서는 System.Management.dll 어셈블리를 참조해야만 한다.

앞서 WMI Infrastructure에서 간단히 설명했듯이 WMI에 액세스하는 것은 CIMOM 서비스에 접근하는 것이며 이는 WMI에서 제공하는 COM API 혹은 Scripting API를 통해서이다. System.Management 네임스페이스에서 제공하는 여러 클래스들은 Scripting API를 닷넷 클래스로서 보다 사용하기 쉽게 감싸놓은(wrapping) 클래스 집합을 제공한다. 여기에서 System.Management 네임스페이스의 클래스들을 모두 설명하기엔 무리가 있으므로 핵심적인 주요 클래스들만을 예제 위주로 간략히 살펴보도록 하겠다. 상세한 각 클래스의 속성, 메쏘드들에 대해서는 MSDN 라이브러리를 참고하기 바란다.

ManagementClass 클래스

ManagementClass 클래스는 WMI 클래스를 추상화하는 클래스이며 이 클래스를 통해 WMI 클래스의 여러 정보를 검사하거나 WMI 클래스의 인스턴스를 생성할 수 있다. ManagementClass는 생성자를 통해 어떤 WMI 클래스를 추상화 할 것인가를 결정한다. 다음 코드에서 ManagementClass는 Win32_Service WMI 클래스를 나타낸다.

ManagementClass cls = new ManagementClass(“Win32_Service”);

ManagementClass의 GetInstances 메쏘드는 해당 클래스의 모든 인스턴스를 반환한다. 이 예제는 이미 앞서 코드에서 예를 보였다.
ManagementClass의 Properties, Methods, Qualifiers 속성은 WMI 클래스의 속성, 메쏘드, 한정자를 나타내는데 사용된다. 따라서 이 속성들을 이용하여 클래스가 어떤 속성, 메쏘드, 한정자를 갖는지 알아 낼 수 있다. Properties 속성은 PropertyDataCollection 컬렉션을 통해 PropertyData 목록을 반환하는데, PropertyData 클래스를 통해 WMI 프로퍼티의 이름, 데이터 타입, 프로퍼티의 한정자 역시 알아낼 수 있다. 리스트 5는 Win32_Service WMI 클래스의 모든 프로퍼티에 대해 데이터 타입, 그리고 프로퍼티의 한정자를 표시하는 코드 조각이다.

string clsName "Win32_Service";
ManagementClass cls = new ManagementClass(clsName);

Console.WriteLine("{0} class property information ---------------", clsName);
foreach
(PropertyData prop in cls.Properties) {
    
// show property info.
    
Console.WriteLine("{0} : CIM type = {1},  defined in {2}"
                                      prop.Name, prop.Type, prop.Origin)
;
    
// show property qualifier
    
foreach(QualifierData qualifier in prop.Qualifiers)
        Console.WriteLine(
"\t{0} : {1}", qualifier.Name, qualifier.Value);
}

리스트 5. WMI 클래스 프로퍼티 정보 나열

ManagementClass는 정적 WMI 클래스를 정의하는데도 사용될 수 있다. 물론 앞서 언급한 MOF를 이용하는 것이 바람직하겠지만 코드에 의해 ‘생성’ 되는 클래스 정의에도 사용된다. 리스트 6은 MOF와 MOF 컴파일러를 사용하지 않고 코드를 통해 WMI 클래스를 정의하는 예제를 보여주고 있다. MasoTestClass 클래스를 생성하기 위해 ManagementClass의 인스턴스를 생성하고 __CLASS 시스템 속성에 클래스 명을 설정한 후, 클래스 고유의 속성들을 추가한 후, 이것을 CIM Repository에 저장(Put 메쏘드)하면 된다. 화면 4는 CIM Studio를 통해 추가된 MasoTestClass 클래스를 확인하는 것을 보여주고 있다.

리스트 6에서 생성한 MasoTestClass는 정적 클래스이다. 즉, 이 클래스는 dynamic 한정자와 provider 한정자를 명시하지 않았기 때문인데, 동적 인스턴스를 생성하는 동적 클래스는 WMI 프로바이더를 구현해야만 작성이 가능하다. 하지만 닷넷 프레임워크는 이러한 한정자 추가와 WMI 프로바이더 작성 작업을 모두 프레임워크 내에서 작성해 준다. 동적 클래스에 대한 상세한 예제는 잠시 후에 다루기로 하자.

어찌되었건 이렇게 생성된 클래스의 인스턴스를 만드는 것은 WMI 테스터나, CIM Studio에서 인스턴스 추가 기능을 통해 인스턴스를 추가할 수 있다. 추가되는 인스턴스는 정적 인스턴스이기 때문에 CIM Repository에 저장된다. 잠시 후에 닷넷 코드를 통해 정적 인스턴스를 생성하는 예제도 살펴보도록 하겠다.

// defining WMI class.
string clsName "MasoTestClass";
ManagementClass cls = new ManagementClass();
cls.Properties["__CLASS"].Value clsName;
// adding property
cls.Properties.Add("MyProperty1", CimType.String, false);
cls.Properties.Add("MyProeprty2", CimType.SInt32, true);
// set key property (required for creating instance)
cls.Properties["MyProperty1"].Qualifiers.Add("key"true);
// save class definition
cls.Put();

리스트 6. 정적(static) WMI 클래스 정의

화면 4. CIM Studio를 통해 추가된 MasoTestClass 클래스

ManagementBaseObject, ManagementObject 클래스

ManagementBaseObject 클래스와 ManagementObject 클래스는 System.Management 네임스페이스에서 가장 핵심적인 역할을 수행하는 클래스이다. ManagementBaseObject는 WMI의 클래스 인스턴스에 대응되는 닷넷 클래스이다. WMI 인스턴스를 5개 획득했다면 ManagementBaseObject 클래스의 인스턴스 5개에 대한 참조를 가지고 있다는 말과 같다. ManagementObject 클래스는 ManagementBaseObject에서 파생된 클래스이다. ManagementBaseObject 클래스는 new 연산자나 Activator.CreateInstance() 메쏘드를 통해 인스턴스를 생성할 수 없는 반면 ManagementObject 클래스는 new를 통해 인스턴스를 생성할 수 있다. 따라서 WMI 클래스를 통하지 않고 직접 하나의 WMI 인스턴스를 얻어내는 것도 가능하다. 다음 코드는 Win32_LogicalDisk WMI 클래스의 C: 드라이브 인스턴스 하나를 직접 얻어내는 코드이다.

// ManagementObject 클래스를 통해 WMI 클래스 인스턴스를 구한다.
ManagementObject disk = new ManagementObject("Win32_LogicalDisk.deviceid='c:'");
disk.Get();
ulong 
total ((ulong)(disk["size"])) / (1024*1024);
ulong 
free ((ulong)(disk["freespace"])) / (1024*1024);
Console.WriteLine("{0}  Total = {1:###,##0} MB   Available = {2:###,##0} MB", disk["Caption"], total, free);

또 한가지 두 클래스가 다른 점은 ManagementBaseObject는 수정된 사항을 다시 CIM Repository에 저장할 수 없는 반면 ManagementObject는 저장이 가능하다. 리스트 6의 코드 뒤에 다음 코드를 추가하여 수행하면 새로운 MasoTestClass의 ‘정적’ 인스턴스가 생성된다. CIM Studio나 WMI 테스터를 통해 생성된 인스턴스를 확인해 보기 바란다.

// Now.. add instance
ManagementObject obj cls.CreateInstance();
// adding proeprty value
obj.Properties["MyProperty1"].Value "Test Value !";
// save instance
obj.Put();

위 코드처럼 새로운 인스턴스를 생성하는 것 외에도 기존에 존재하는 인스턴스를 수정하는 것도 가능하다. 물론 이렇게 인스턴스를 수정하는 것이 가능하기 위해서는 WMI 클래스가 수정 기능을 지원(클래스의 SupportUpdate 한정자의 값이 true)해야만 하고 속성이 읽기 전용이 아니어야 할 것이다.

ManagementScope, ManagementPath 클래스

리스트 6의 코드는 MasoTestClass를 root/cimv2 네임스페이스에 생성해 버린다. ManagementClass와 ManagementObject의 생성자(constructor)는 명확하게 WMI 네임스페이스나 WMI PATH가 명시되지 않으면 로컬 컴퓨터의 root/cimv2 네임스페이스를 사용하기 때문이다. 원격 컴퓨터나 다른 네임스페이스를 사용하고자 한다면 추가적인 작업이 필요하다.

ManagementScope 클래스는 WMI 네임스페이스에 대응되는 닷넷 클래스이다. WMI의 보안 모델은 네임스페이스 별로 이루어 지므로 ManagementScope 클래스는 네임스페이스에 연결하기 위한 접속에 관련된 프로퍼티와 메쏘드들도 제공한다. 예를 들어 원격 컴퓨터의 WMI에 연결하기 위해서는 원격 컴퓨터의 이름과 사용자 ID, 암호를 제공할 필요가 있다(AD를 사용하지 않는다면). 이를 위해 ManagementScope 클래스는 ConnectionOption 클래스와 더불어 접속에 필요한 컴퓨터, 네임스페이스, 보안정보를 기술할 수 있도록 되어 있다.

ManagementPath 클래스는 WMI PATH에 대한 유틸리티 성 클래스로서 주어진 문자열 WMI PATH에 대해 네임스페이스만을 분리해 낸다던가, 주어진 PATH가 네임스페이스를 나타내는지, 클래스를 나타내는지 혹은 인스턴스를 나타내는지 알려주는 프로퍼티를 가지고 있다.

ManagementObject 클래스와 ManagementClass 클래스의 생성자는 모두 ManagementScope 객체와 ManagementPath 객체를 매개변수로 취하는 생성자를 가지고 있다. 따라서 지금까지 보아온 예제들처럼 단순히 네임스페이스, 경로를 문자열로 줄 수도 있지만 보안 정보가 필요한 경우에는 ManagementScope 객체와 ManagementPath 객체를 모두 사용해야 한다. 리스트 7은 원격 컴퓨터에 설치된 Hot-Fix를 열거하는 예제 코드로서 ManagementScope 객체를 사용하는 예를 보여주고 있다.

ConnectionOptions option = new ConnectionOptions();
option.Username "yourid";
option.Password "yourpassword";

ManagementScope scope = new ManagementScope(@"\\svr1\root\cimv2", option);
scope.Connect();

ManagementPath path = new ManagementPath("Win32_QuickFixEngineering");
ManagementClass cls = new ManagementClass(scope, path, null);
ManagementObjectCollection col cls.GetInstances();

foreach
(ManagementObject obj in col) {
    
string desc (string)obj["Description"];
    if 
(desc !"")
        Console.WriteLine(desc)
;
}

리스트 7. 원격 컴퓨터에 설치된 Hot-Fix를 열거하는 코드

리스트 6의 코드가 root/cimv2 네임스페이스에 MasoTestClass를 생성하므로 다른 네임스페이스에 클래스를 생성하고자 한다면 ManagementClass의 인스턴스를 생성하는 코드를 다음과 같이 수정하면 된다.

ManagementClass cls 
     new 
ManagementClass(new ManagementScope("\\.\root\otherNamesapce"), null);

WqlObjectQuery, ManagementObjectSearcher 클래스

System.Managment 네임스페이스는 CIM Repository에 대한 검색 기능을 지원한다. 물론 이 검색이란 것이 WQL에 의한 것임은 말할 필요도 없다. WQL 쿼리를 기술하기 위해서는 WqlObjectQuery 클래스를 사용하면 된다. WqlObjectQuery 클래스는 WQL을 문자열로 기술하는 매우 간단한 클래스이지만 이 클래스에서 파생된 몇몇 클래스는 WQL 구문 자체를 모르더라도 손쉽게 WQL을 생성할 수 있도록 해준다. 이들 클래스에 대한 상세한 내용은 MSDN 라이브러리를 참고하기 바란다.

WqlObjectQuery 클래스를 통해 WQL을 기술했다면 이 WQL을 이용하여 실제 검색을 수행하는 클래스는 ManagementObjectSearcher 클래스이다. 이 클래스는 ManagementScope 객체와 WqlObjectQuery 객체를 매개변수로 취하는 생성자를 통해 조건 검색을 수행할 수 있다. ManagementObjectSearcher 클래스의 인스턴스를 생성한 후에는 Get 메쏘드를 호출하여 조회 결과로서 ManagementObject 혹은 ManagementClass 컬렉션을 얻을 수 있다. 다음 코드는 MSDN 라이브러의 예제 코드로서 시스템에 공유된 폴더에 대한 정보를 나열하는 Win32_Share WMI 클래스의 모든 인스턴스를 반환한다.

WqlObjectQuery objectQuery = new WqlObjectQuery("select * from Win32_Share");
ManagementObjectSearcher searcher =
    new 
ManagementObjectSearcher(objectQuery);

foreach 
(ManagementObject share in searcher.Get()) { 
    Console.WriteLine(
"Share = " + share["Name"]);
}

MangementEventWatcher 클래스

ManagementEventWatcher 클래스는 WMI의 이벤트를 위한 클래스이다. 이 클래스는 이벤트를 조회하는 WqlObjectQuery 객체를 매개변수로 인스턴스가 생성된다. 이 클래스는 이벤트의 수신을 시작/중단하는 Start()/Stop() 메쏘드를 가지고 있으며 다음 이벤트 발생까지 기다리는 WaitForNextEvent() 메쏘드도 가지고 있다. 더욱이 편리한 것은 WMI 이벤트가 발생되면 이벤트를 처리하는 핸들러를 추가할 수 있다. ManagementEventWatcher 클래스의 EventArrived 이벤트 속성에 delegate를 추가함으로써 이벤트가 발생할 때마다 특정한 작업을 수행할 수도 있다. 이 클래스에 대한 예제는 MSDN 라이브러리에서 충분히 등장하므로 여기에선 생략하겠다.

.NET Instrumentation using WMI

WMI는 관리(management)와 계측(instrumentation) 서비스를 제공하는 윈도우 인프라 시스템 중 하나이다. 지금까지 필자가 살펴본 내용을 음미해 보면 대부분 하드웨어, 운영체제, 소프트웨어 ‘관리’ 측면에서 WMI의 사용법이 위주였다고 할 수 있다. 닷넷의 System.Management 네임스페이스와 관련 클래스들도 모두 이러한 ‘관리’를 위한 클래스들을 위주로 설명했다. 하지만 필자가 정말 이 칼럼을 통해 하고 싶은 말은 그러한 관리의 대상이 되는 정보들을 WMI 를 통해 어떻게 제공하는가 이다. 이 이야기가 하고 싶어서 지금까지 WMI 내부 구조와 System.Management 네임스페이스를 설명한 것이다. 그렇다면 WMI를 어떻게 계측에 사용할 수 있는가를 살펴보도록 하자.

Instrumentation

Instrumentation 이란 용어를 영한 사전에서 찾아보면 악기의 ‘연주’ 정도로 해석된다. ‘연주’라는 뜻으로는 도저히 instrumentation 이라는 용어가 갖는 뉘앙스와 연결이 안 된다. 다시 IT 용어 사전을 찾아 보았다. 대개 instrumentation 앞에 명사가 붙어서 ‘~ 계측’ 정도로 해석이 되어 있다. 별로 맘에 들진 않지만 그래도 ‘연주’ 보다는 ‘계측’이라는 단어가 좀 더 명확한 듯 하다.

앞으로 설명할 계측이란 소프트웨어(시스템, 어플리케이션 등)의 작동 상황을 관찰하고 측정하는 행위를 말하는 것이다. 예를 들어, 웹 서버가 현재 다운되지 않고 작동 중이며 IIS가 1분에 몇 개의 HTTP Request를 처리하고 있는지 관찰하고 측정한다면 이것이 웹 서버에 대한 계측(instrumentation)이 될 것이다.

일반적으로 Instrumentation을 수행하는 방법과 도구는 다양하다. Event Log를 통해 계측 대상이 생성하는 로그 메시지를 관찰하거나, Win32 Performance Counter를 사용해 시스템/어플리케이션의 성능을 모니터링 해 볼 수도 있다. 이렇게 일반적인 도구와 방법을 사용하거나, 어플리케이션에서 printf 문이나 OutputDebugMenssage API, 혹은 닷넷의 Trace.WriteLine 메쏘드를 통해 로그를 파일에 기록하는 프로그래밍적인 방법도 생각해 볼 수 있다. 이에 반해 WMI는 표준화되고 단일화된 인터페이스를 통해 Instrumentation을 보다 쉽게 해주는 계측의 강력한 도구로서 사용될 수 있다.

계측의 핵심은 운영 중인, 작동중인 시스템, 어플리케이션에 대한 정보를 제공하는 것에 있다. 개발 시나 테스트 시에는 디버깅이나 기타 테스트 도구를 이용하여 이러한 계측을 수행할 수 있다. 오픈 된지 1개월 정도 지난 웹 어플리케이션에 대해, 높은 윗분이 시스템이 잘 작동되고 있는가를 알 수 있는 수치적 정보를 달라고 한다면, 대략 ‘뷁’스런 일이 아닐 수 없다.

Instrumenation은 어플리케이션/시스템이 설계되고 구현될 때부터 고려를 해야 하는 사항이다. 시스템의 현재 상태와 작동 현황에 대한 정보를 제공하도록 설계/구현된 시스템은 추후 유지/보수가 편리하며 시스템에서 발생하는 문제를 즉각 파악할 수 있도록 해준다.

System.Management.dll 어셈블리의 System.Management.Intrumentation 네임스페이스는 WMI를 통해 계측을 수행할 수 있도록 다양한 기능을 제공한다. 닷넷 클래스를 WMI 클래스로 변환해 주어 닷넷 클래스의 인스턴스가 WMI 인스턴스로 매핑될 수 있도록 WMI 프로바이더를 제공해주며, 닷넷 어플리케이션 상에서 WMI 이벤트를 발생할 수 있도록 해준다. 이와 같은 기능에 복잡한 코드는 필요하지 않으며 단순히 특성(attribute)을 클래스에 추가함으로써 손쉽게 instrumentation이 가능하게 해준다는데 더 큰 매력이 있다. 심지어는 WMI 내부에 대한 내용을 전혀 모르더라도 말이다(필자주: 말이 그렇지 WMI를 전혀 모르고서는 좀 곤란하다...).

Exposing Management Data

System.Management.Instrumentation 네임스페이스의 InstrumentationClass 특성은 닷넷 클래스를 WMI 클래스로 매핑 해 준다. 리스트 6과 같이 단순한 정적 클래스가 아닌, 동적으로 인스턴스를 생성/조회하는 동적 클래스로 말이다. 더욱이 훌륭한 것은 닷넷 클래스의 클래스 상속 구조를 모두 WMI 상속 구조로 변환해 준다는 것이다. 단지 아쉬운 점은 닷넷의 특성(attribute)를 WMI의 한정자(qualifier)로 변환시켜주지는 못한다. 하지만 WMI 클래스, 한정자, 동적 클래스 등등의 사항을 잘 모르더라도 WMI 를 통해 instrumentation 정보를 관리 프로그램에게 제공할 수 있다는 것은 큰 매력이다.

닷넷 클래스를 WMI 클래스로 정의하고자 한다면 클래스에 InstrumentationClass 특성을 추가한다. 이때, InstrumentationType을 Instance로 설정하면 WMI 인스턴스로서 정보가 제공된다. InstrumentationClass 특성이 추가되면 해당 닷넷 클래스의 모든 public 속성과 필드가 WMI 속성으로 매핑되며 닷넷 클래스의 public 메쏘드는 WMI 메쏘드로 매핑 된다. 이 매핑에서 일부 지원하지 않는 데이터 타입과 객체에 대한 참조들이 존재하는데 대부분의 경우 문자열이나 숫자 혹은 문자열의 배열, 숫자의 배열 정도의 타입을 사용하므로 큰 문제는 없다.

[InstrumentationClass(InstrumentationType.Instance)]
public class MyClass
{
    
public string MyData1;
    public int 
MyData2;
}

이렇게 클래스를 정의 한 후, 일반 클래스의 인스턴스를 만들듯이 MyClass의 인스턴스를 만들면 된다. 그리고 이 MyClass의 인스턴스가 WMI를 통해 액세스 되도록 한다면 Instrumentation 클래스의 스태틱(static) 메쏘드인 Publish를 호출하면 된다. 여러 개의 WMI 인스턴스를 만들고자 한다면 원하는 만큼의 MyClass 클래스의 인스턴스를 생성하고 각각에 대해 Publish를 호출하면 된다(당연 빤쭈 되겠다).

MyClass obj = new MyClass();
obj.MyData1 "My Data Test";
obj.MyData2 99;
Instrumentation.Publish(obj);

Publish 메소드를 통해 퍼블리쉬 된 객체들은 MyClass WMI 클래스의 인스턴스 조회에서 모두 나타나게 된다. 한가지 주의할 사항은 MyClass 클래스와 그 인스턴스들이 dynamic 인스턴스이기 때문에 이 코드를 담는 프로세스가 시작되지 않았다면 인스턴스는 조회되지 않는 다는 점이다. 동적 인스턴스 이기 때문에 얻을 수 있는 장점은 인스턴스의 데이터가 변경되면 그 변화 사항은 즉시 반영된다. 여기서 말하는 ‘즉시’라는 말은 WMI 클라이언트가 인스턴스를 조회할 때마다 인스턴스의 값을 읽어간다는 것이다. 다시 말해 WMI 인스턴스는 하나 존재하더라도 해당 인스턴스가 제공하는 데이터(속성)는 매번 변할 수 있다는 것이다.

이러한 점을 통해 어플리케이션의 현재 작동 상태를 측정할 수 있다. 예를 들어, 소켓을 이용한 네트워크 서버가 현재 연결되어 있는 클라이언트가 몇 개나 되며 그들의 IP 정보를 WMI를 통해 제공할 수 있다. 간단한 모니터링 프로그램을 작성하거나 WMI 유틸리티 혹은 고급 모니터링 도구(Microsoft Operation Monitor)를 통해 이러한 정보를 ‘계측’할 수 있다. 다음 코드 조각은 구체적인 예를 보여주고 있다. 소켓 클라이언트가 접속하면 클라이언트의 정보(IP, 사용자 ID 등)를 WMI 인스턴스로 등록하고 클라이언트의 접속이 종료되면 등록된 WMI 인스턴스를 제거(Instrumentation.Revoke 메쏘드)한다. 따라서 WMI ClientInfo 클래스의 인스턴스를 조회하는 WQL을 작성하면 현재 접속되어 있는 클라이언트의 다양한 정보 등을 얻을 수 있으며, WMI의 인스턴스 생성/삭제 이벤트에 가입(subscription)하면 클라이언트의 접속 이벤트를 얻을 수도 있다.

// WMI 클래스 정의 
[InstrumentationClass(InstrumentationType.Instance)]
public class ClientInfo
{
   
public string IP;
   public string 
UserID;
   public int 
WorkerThreadID;
}

// 클라이언트마다 Worker 클래스가 하나씩 만들어져 처리한다고 가정하자.
public class Worker
{
   
private ClientInfo info;

   
// 클라이언트 접속 시 코드 ---------------------------------------
   
public void OnAccept(Socket clientSocket)
   {
      ClientInfo info 
= new ClientInfo();
      
info.IP clientSocket.RemoteEndPoint.ToString();
      
info.WorkerThreadID AppDomain.GetCurrentThreadId();
      
// 소켓에서 사용자 정보를 읽는다고 가정..
      
info.UserID GetUserInfoFromFirstPacket();
      
// WMI에 인스턴스 퍼블리쉬
      
Instrumentation.Publish(info);
      
// 기타 다른 작업 수행… (생략)
   
}

   
// 중략......

   // 클라이언트 접속 종료 시 WMI에서 인스턴스 제거 ---------------------
   
public void OnClose()
   {
      Instrumentation.Revoke(info)
;
   
}
}

만약 닷넷을 사용하지 않고 WMI 를 통해 동적 데이터를 제공하고자 한다면 많은 작업이 필요하다. MOF를 이용하여 클래스를 정의하고, WMI 프로바이더를 ATL 과 C++를 이용해 작성해야 한다. 하지만 닷넷의 System.Management.Instrumentation 네임스페이스의 클래스들은 이러한 작업을 매우 간단한 선언적(declarative) 방법으로서 가능하게 해준다. 멋지지 않은가? (아니면 말고… -_-)

Exposing Management Event

WMI 이벤트를 발생하는 것 역시 매우 쉽다. 닷넷 클래스를 WMI 커스텀 이벤트 클래스로 매핑하는 작업 역시 닷넷 클래스에 InstrumentationClass 특성을 추가하는 것이다. 이번에는 InstrumentationType을 Event로 설정하기만 하면 된다. 이벤트의 발생 역시 매우 쉽다. 닷넷 클래스의 인스턴스를 생성하고 Publish와 비슷하게 Instrumentation의 Fire 메쏘드를 호출하면 WMI 이벤트가 발생한다.

[InstrumentationClass(InstrumentationType.Event)]
public class MyEvent
{
   public string EventInfo;
}

MyEvent evt 
= new MyEvent();
evt.EventInfo "Your event information";
Instrumentation.Fire(evt);

WMI Installation

예상 할 수 있듯이, 닷넷 클래스가 WMI 클래스로서 사용되기 위해서는 설치 과정이 필요하다. 프로그램적인 코딩이 매우 간단하듯이 설치 역시 매우 간단히 해결할 수 있다. 닷넷 instrumentation의 설치는 항상 어셈블리 단위로 이루어 진다. 따라서 해당 어셈블리가 instrumentation에 사용되는지 여부를 표시해야 한다. 이를 위해 Instrumented 특성이 제공되며 이 특성에는 어셈블리 내에 정의된 WMI 클래스가 어떤 WMI 네임스페이스에 저장될 것인지 역시 명시할 수 있다. 다음 Instrumented 특성은 해당 어셈블리가 WMI 클래스(이벤트 클래스 포함) 정의를 포함하고 있으며 이 클래스는 root/MyTest 네임스페이스에 저장될 것을 명시하고 있다. 닷넷 프레임워크는 root\MyTest 네임스페이스의 존재 유무를 검사하여 존재하지 않는다면 새로운 네임스페이스를 만든다.

[assembly:Instrumented("root/MyTest")]

닷넷 Instrumentation을 사용하기 위한 최소의 조건은 이 정도 이다. 어셈블리에 Instrumented 특성이 명시되어 있고, InstrumentationClass 특성이 명시된 클래스가 존재한다면 Instrumentation.Publish, Instrumentation.Fire 메소드는 런타임에 임시 MOF 파일을 생성하고 컴파일 하여 WMI 클래스 정의를 CIM Repository에 만든다(필자주: 이 메쏘드들이 최초로 호출되는 시점에서). 따라서 리스트 8의 코드는 잘 작동하며 CIM Studio나 WMI 테스터를 통해 작동 상황을 확인할 수 있다.

using System;
using 
System.Management;
using 
System.Management.Instrumentation;

[assembly: Instrumented("root/MyTest")]

[InstrumentationClass(InstrumentationType.Instance)]
public class MyClass
{
    
public string MyData1;
    public int 
MyData2;
}

[InstrumentationClass(InstrumentationType.Event)]
public class MyEvent
{
    
public string EventInfo;
}

public class MainClass
{
    
public static void Main()
    {
        MyClass obj 
= new MyClass();
        
obj.MyData1 "My Data Test";
        
obj.MyData2 99;
        
Instrumentation.Publish(obj);

        
MyEvent evt = new MyEvent();
        
evt.EventInfo "Your event information";
        
Instrumentation.Fire(evt);

        
// wait...
        
Console.ReadLine();
    
}
}

리스트 8. 간단한 .NET Instrumentation 예제

한가지 고려할 사항은 닷넷 프레임워크가 제공하는 WMI 자동 설치에 의존해서는 안 된다는 것이다. 자동 설치 기능은 개발 시 편의를 제공하기 위함일 뿐이다. 만약 실제 배포를 자동 설치에 의존한다면, 최초에 어플리케이션이 수행되고 Instrumentation 클래스의 Publish/Revoke/Fire 메소드를 호출할 때까지는 WMI 스키마가 전혀 생성되지 않기 때문이다.

따라서 프로그램 수행 시가 아닌 프로그램 설치 시에 WMI 스키마가 생성되도록 하는 것이 좋다. 이러한 설치를 지원하기 위해 System.Management.Instrumentation 네임스페이스에는 ManagementInstaller 클래스가 제공되며 이 클래스는 어셈블리가 정의하는 WMI 스키마를 생성해 준다. 이 클래스를 여러분의 Install 코드에 사용할 수도 있으며 다른 설치 작업이 없다면 DefaultManagementProjectInstaller 클래스를 상속받아 다음과 같이 두 줄의 코드를 추가함으로써 간단히 해결할 수 있다. 이렇게 생성된 어셈블리(DLL 혹은 EXE)를 InstallUtil.exe를 통해 설치하면 WMI 스키마가 생성된다.

[System.ComponentModel.RunInstaller(true)]
public class MyInstaller : DefaultManagementProjectInstaller {}

Instrumentation 응용

Instrumentation은 어플리케이션의 작동 여부를 감시하는 중요한 수단이다. 물론 Instrumentation을 사용하지 않고 성능 카운터(performance counter)나 추적 로그(trace log)를 통해 어플리케이션의 정상 작동 여부를 판단하는 방법도 있다. 하지만 성능 카운터는 특정 숫자 값에 의존하는 계측의 한 방법일 뿐이며, 추적 로그는 발생되는 로그를 다시 재 가공해야 하는 경우가 매우 많다. 반면 WMI를 이용한 Instrumentation은 다양한 정보를 표준화된 인터페이스를 통해 어플리케이션의 현재 상태를 알아보는 방법을 제공한다.

  • 현재 수행중인 서버에 접속한 사용자는 누구인가?
  • 현재 서버에서 작동중인 비즈니스 로직 컴포넌트는 모두 몇 개인가?
  • 각 컴포넌트의 평균 수명 주기는 어떠한가?
  • 특정 사용자에 대해 로그온/로그오프 이벤트를 받을 수 있는가?

이러한 질문에 대한 대답은 WMI와 System.Management.dll가 가지고 있다.

마지막으로 Instrumentation은 반드시 어플리케이션 설계와 개발단계에서 고려되어야 하며, 개발 시에 어플리케이션의 작동 상황을 모니터링 할 수 있는 방법(WMI와 같은)이 제공되어져야 함을 강조하는 바이다.

참고문헌


여기까지 모두 읽으셨다면 정말 수고 많으셨습니다. 도움이 되셨나요?



Comments (read-only)
#re: 2004년 8월호 닷넷 칼럼 :: Management & Instrumentation of Your Own Application(WMI) / 임종수 / 2006-10-28 오전 9:55:00
본 내용을 몇번읽었는데.. 난 왜 이해가 안되는지..리스트 6의 코딩이 정상 실행되기 위해서 원격지 컴에 모가 설치 되어 있어야 하는지요 ??? 모르는 생소한거는 (나안테는다 생소한것임)많이 알았는데 정작 그것에 대한 내용은 없어서요^^
#re: 2004년 8월호 닷넷 칼럼 :: Management & Instrumentation of Your Own Application(WMI) / 블로그쥔장 / 2006-10-29 오후 11:59:00
임종수님... 제가 도와드릴 수 있는 일은 거의 다 도와 드린 것으로 보입니다.
마지막으로 남은 건 제가 직접가서 왜 원하는 것이 안되는지 설명 드리는 것 밖에는 없는듯 하군요...
이해가 안된다고 불평하시기 전에 좀 더 다른 자료나 기초적인 것을 이해하려고 노력하는 것이 필요하지 않을까요?
데브피아에 질문 올려놓은 것을 보아 하니... 원하시는 코드는 리스트6이 아니라 리스트7이겠지요.
리스트7이 작동하기 위해서 원격 컴퓨터에 설치되어야 할 것은 없습니다.
원격 컴퓨터의 방화벽을 끄시고 관리자 계정으로 시도해 보십시요.
아무것도 하지 않고 얻어지는 것은 없답니다.
저도 19년간 수십, 수백만 라인의 코드를 작성한 경험으로 얻어낸 것이니까요...
화이팅 입니다!!!
(기분 나쁘셨다면 죄송합니다. 제 글이 원래 좀 까칠해서요.... -_-;)
#re: 2004년 8월호 닷넷 칼럼 :: Management & Instrumentation of Your Own Application(WMI) / 쿠쿠쿠111 / 2007-01-16 오전 11:43:00
까칠하지^^ 원래 몸에 좋은 약이 입에 쓴 법이지 ㅋㅋ
그나저나 글내용은 스크롤로 확 댕겨보고, 게시판 놀이만 하고 있으니,,,반성해야쥐
#re: 2004년 8월호 닷넷 칼럼 :: Management & Instrumentation of Your Own Application(WMI) / 조승태 / 2008-02-21 오후 4:24:00
후아~
아침 부터 짬짬이 읽어서 오후 4시가 넘어서야 1독이 끝났네요 ^^;;
업무와 병행하다 보니..ㅋㅋ

한마디로 후덜덜이네요.
WMI,, 제가 써볼 일이 있을 지는 모르지만 큰 프로젝트에는 꼭 한번 고려해봐야 할 것 같네요.
회사에서 SQL server를 나름 크게 운영하고 있는데, 성능 추적 tool로 작성해 보는 것도 재밌을 것 같구요.

좋은 글 잘 보았습니다.
여긴 추천 버튼 없나요? ㅋㅋ ㅎㅎ
#re: 2004년 8월호 닷넷 칼럼 :: Management & Instrumentation of Your Own Application(WMI) / 블로그쥔장 / 2008-02-21 오후 4:47:00
조승태//
남겨주신 글 하나 하나가 추천입니다.
조잡한 글이지만 좋은 평가해 주셔서 감사합니다.