.NET 환경과 ADSI 프로그래밍의 개요

등록일시: 2002-03-23 04:07,  수정일시: 2018-04-07 11:12
조회수: 18,246
본문은 최초 작성 이후, 약 22년 이상 지난 문서입니다. 일부 내용은 최근의 현실과 맞지 않거나 동떨어져 있을 수 있으며 문서 내용에 오류가 존재할 수도 있습니다. 또한 본문을 작성하던 당시 필자의 의견과 현재의 의견에 많은 차이가 존재할 수도 있습니다. 이 점, 참고하시기 바랍니다.

본문에서는 .NET 환경에서 새롭게 변경된 ADSI 프로그래밍에 대해서 간단하게 살펴본다. 결론부터 말하자면 기본적으로 ADSI 자체는 변한 것이 아무 것도 없다. 다만 방법론의 관점에서 봤을 때, 각각의 프로그래밍 언어마다 각각의 방법으로 접근하던 프로그래밍 방식이 .NET 프레임워크가 등장함으로써 하나의 CLR로 정리되면서 ADSI에 대한 접근 방법 자체가 일원화된 것이라고 생각할 수 있다.

사실 .NET 등장 이전에 사용되던 방식, 즉 프로그래밍 언어별로 제각각이었던 ADSI 프로그래밍 방식도 프로그래밍 언어마다 제각각의 방법으로 COM/COM+ 개체 및 인터페이스에 접근했던 것이 그 원인이었을 뿐, 본질적으로 ADSI 자체가 특정 프로그래밍 언어에 종속되어 있었다고 평가할 수는 없을 것이다.

이런 ADSI의 특성은 ADSI의 본질에서 그 이유를 찾을 수 있을 것 같다. 필자의 사견으로는 여러 가지 점들로 미루어 보아 마이크로소프트가 ADSI를 통해서 얻고자 했던 것이 디렉터리 시스템(Directory System)에 대한 접근 방식의 일원화였다고 생각한다. 이것은 ODBC가 관계형 데이타베이스를 대상으로 이루고자 했던 이상과 거의 정확하게 같은 것이다. 물론 마이크로소프트가 UDA(Universal Data Access)라는 개념을 들고 나와서 그 대상이 관계형 데이타베이스를 넘어선 좀 더 포괄적인 그 무엇으로 바뀌었다는 점을 고려하지 않는다면 말이다. 그러나, 이 이야기는 엄밀히 말해서 ODBC에 관한 얘기가 아닌 OLE DB에 관한 얘기이긴 하다.

각설하고 CLR에서 ADSI와 관련된 거의 모든 클래스는 System.DirectoryServices 네임스페이스에서 찾을 수 있다. 그중에서도 가장 핵심적인 클래스는 DirectoryEntry 클래스와 DirectoryEntries 클래스로, 굳이 여기에 몇 가지 더 추가해본다면 DirectorySearcher 클래스와 SearchResult 클래스, 그리고 마지막으로 SearchResultCollection 클래스 정도가 될 것이다.

본문에서는 이 클래스들의 전체 멤버를 설명하거나 하지는 않을 것이다. 필자 역시도 아직 이 클래스들에 대해서 그 정도로 많이 알지 못할 뿐더러, 그런 식의 글은 그저 분량을 채우기 위해서라면 모를까 전혀 도움이 되지 않을 것이라고 믿고 있다.

대신 필자는 Microsoft Visual C#으로 간단한 프로그램을 하나 작성했다. 아래 그림은 그 프로그램을 필자의 작업용 머신에서 실행한 모습이다. 눈치채신 분이 있으실지도 모르겠지만 사실 이 프로그램이 수행하는 작업은 EgoCube.IISWebAdmin 컴포넌트의 GetWebServerList() 메서드가 수행하는 작업과 거의 완벽하게 동일한 것이다. 즉, 프로그램 자신이 실행되고 있는 머신의 IIS에 존재하는 모든 가상 웹 서버의 정보를 얻고 이를 정리하여 리스트뷰 컨트롤을 사용하여 보여준다.

이 샘플 프로그램과 GetWebServerList() 메서드를 서로 비교해 보는 것도 나름 좋을 것이다. 이 프로그램의 이름은 EgoCube ADSI for IIS Sample Program이라고 필자가 취향대로 지어놨지만 이는 전혀 중요한 사항이 아니므로 글에서는 그냥 샘플 프로그램이라고 부르도록 하겠다.

그리고, 이 샘플 프로그램을 ASP.NET으로 만들지 않고 Windows 응용 프로그램으로 만든 이유는, ASP.NET으로 만들 경우 사용자 권한에 관한 문제를 처리해야 하기 때문에 ADSI 프로그래밍 자체에 집중하기 어렵기 때문이다. Windows 응용 프로그램으로 만들면 그런 문제에 관해서는 신경쓸 필요가 없기 때문에 편리하다. 대신 이 샘플 프로그램은 관리자 그룹에 속한 계정으로 로그인해서 실행해야만 올바르게 동작할 것이다.

EgoCube ADSI for IIS Sample Program

다음 링크는 Microsoft Visual C#으로 작성된 샘플 프로그램의 소스 코드와 실행 파일이다. 영문판 Visual Studio.NET Enterprise Architect RTM 버전을 사용해서 작성하고 컴파일했는데, 참고로 필자의 작업용 머신에 설치된 .NET 프레임워크 버전은 1.0.3705이다. 하지만 Beta 2 버전을 사용하고 있거나 .NET 프레임워크만 설치해서 사용하는 분들도 실행에 별다른 무리는 없을 것이다.

사실상 ADSI에 관련된 코드의 분량 자체가 매우 적고 그 내용 또한 매우 간단하므로 프로그램 실행에 문제가 있는 분들은 아예 이 샘플 프로그램의 코드를 참고로 직접 프로그램을 작성해보는 것도 좋을 것이다.

여러분들은 필자가 Taeyo's ASP & ASP.NET에서 설명했던 IADs 인터페이스와 IADsContainer 인터페이스를 기억하고 있을 것이다. 이 인터페이스들은 순서대로 각각 DirectoryEntry 클래스와 DirectoryEntries 클래스에 대응된다. 결국, 이 두 개의 클래스야말로 .NET ADSI 프로그래밍의 핵심이라고 말할 수 있는 것이다. 그러므로, 본문에서는 여러분이 IADs 인터페이스와 IADsContainer 인터페이스에 대한 필자의 글을 이미 읽어봤다는 가정하에서 이야기를 진행해 나가도록 하겠다.

샘플 프로그램의 핵심 코드는 대부분 Form1_Load() 이벤트 헨들러에 기술되어져 있으며, 주석을 제외한다면 스무 줄이 조금 넘는 정도의 분량이다. 간단한 유틸리티 메서드인 GetStatusDesc() 메서드를 논외로 한다면, 기본적으로 Form1_Load() 이벤트 헨들러 이외의 모든 부분은 ADSI와는 전혀 상관 없는 코드들이다.

먼저 DirectoryEntry 클래스와 DirectoryEntries 클래스를 사용하기 위해서는 System.DirectoryServices.dll 파일을 참조해야 한다. Solution Explorer에서 References 노드를 마우스 오른쪽 버튼으로 클릭하면 나타나는 팝업 메뉴에서 Add Reference... 메뉴를 선택하면 다음의 그림과 같이 Add Reference 대화 상자가 나타나는데, 여기에서 해당 DLL의 참조를 추가할 수 있다. 물론 다운로드 받은 샘플 프로그램의 프로젝트에는 이미 이러한 설정되어 있을 것이다.

'Add Reference' 대화 상자

그 뒤에 다음과 같이 using 문을 사용해서 System.DirectoryServices 네임스페이스 선언을 추가해야 한다.

...
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using System.DirectoryServices;    // ADSI 프로그래밍을 위한 네임스페이스 선언
...

이제 ADSI 프로그래밍을 위해 필요한 사전 준비에 관한 설명이 모두 끝났으므로, 실제로 ADSI와 관련된 코드를 담고 있는 Form1_Load() 이벤트 헨들러를 한 줄씩 차분히 살펴보도록 하자.

...
// DirectoryEntry 개체를 선언하고 IIsWebService 개체를 얻는다.
DirectoryEntry IISRoot = new DirectoryEntry("IIS://LocalHost/W3SVC");
...

일단 DirectoryEntry 클래스형 개체인 IISRoot를 선언하고 new 연산자를 사용해서 DirectoryEntry 클래스의 생성자를 호출해서 바로 개체를 할당한다. 이 때 생성자에 넘겨주는 문자열은 바로 ADsPath이다. 이 프로그램의 목적은 IIS에 존재하는 모든 가상 웹 서버의 정보를 얻는 것이므로 가상 웹 서버의 부모격인 웹 서비스 개체를 얻기 위해 웹 서비스의 ADsPath를 입력했다.

이 말의 뜻이 잘 이해가 되지 않는다면 메타베이스의 구조를 다시 한 번 상기해 보기 바란다. 우선 IIS의 전체 구조에서 가장 상위에 존재하는 것은 컴퓨터 자체를 의미하는 IIsComputer 개체이다. 그리고, 그 하위에 FTP 서비스를 의미하는 IIsFtpService 개체와 WWW 서비스를 의미하는 IIsWebService 개체 등이 존재한다. 현재 관심의 대상인 가상 웹 서버 개체는 IIsWebService 개체 하위에 IIsWebServer라는 개체의 형태로 존재하고 있는데, 이미 알고 있는 것처럼 Windows 2000 Server 및 Advenced Server상의 IIS에서는 하나의 웹 서비스 하위에 여러 개의 가상 웹 서버가 동시에 존재할 수 있으므로, 각각의 가상 웹 서버마다 구분을 위한 일련 번호가 붙게 되고, 바로 이 일련 번호가 Index가 되는 것이다.

따라서, 복수의 가상 웹 서버들의 정보를 한 번에 얻기 위해서는 먼저 IIsWebService 개체를 얻어야만 일이 수월해진다. IIsWebService 개체를 얻기 위해서 ADsPath를 구성하는 것은 매우 쉬운 일이다. 이 경우, ADSI 제공자는 당연히 IIS://가 된다. 그리고, 메타베이스의 최상위 개체인 컴퓨터에 관해서 ADsPath를 구성해야 하므로 현재 컴퓨터를 의미하는 LocalHost를 설정하면 된다. 만약, 작업의 대상이 되는 컴퓨터가 현재 컴퓨터가 아니라 네트워크 상의 다른 컴퓨터라면 그 컴퓨터의 NetBIOS 이름이나 IP 주소를 설정하면 된다.

ADsPath → IIS://LocalHost
ADsPath → [IIS Provider]://[Computer]

이런 과정을 거쳐서 구성된 ADsPath에 이번에는 IIsWebService에 관한 구성을 추가해야 하므로 IIsWebService 개체의 네임스페이스 이름인 W3SVC를 ADsPath에 추가한다. 그 결과로 다음과 같은 ADsPath가 만들어진다.

ADsPath → IIS://LocalHost/W3SVC
ADsPath → [IIS Provider]://[Computer]/[IIsWebService]

이렇게 구성한 ADsPath 문자열을 DirectoryEntry 클래스의 생성자에 넘겨주면 IIsWebService의 개체를 얻을 수 있고, 결국 이 코드가 실행되면 DirectoryEntry 클래스형 개체 변수 IISRoot에 IIsWebService 개체의 참조가 할당된다. 이제 그 다음 줄을 살펴보자.

...
// IIsWebService 의 모든 자식 개체를 얻는다.
DirectoryEntries Elements = IISRoot.Children;
...

여기에서 사용되고 있는 Children 프로퍼티는 DirectoryEntries 클래스형으로, 현재 DirectoryEntry 클래스형 개체의 하위에 존재하는 모든 자식 개체들에 대한 참조를 갖고 있다. 따라서, IIsWebService 개체를 참조하고 있는 IISRoot 개체 변수의 Children 프로퍼티에는 우리가 정보를 얻고자 하는 가상 웹 서버, 즉 IIsWebServer 개체들의 참조가 담겨 있을 것이다.

그래서, 이 부분에서는 Children 프로퍼티를 Elements라는 이름으로 생성한 DirectoryEntries 클래스형 변수에 할당하고 다음 작업을 준비하고 있다. 재미있는 점은 DirectoryEntries 클래스가 IEnumerable 인터페이스를 구현하고 있다는 점으로, 이 때문에 DirectoryEntries 클래스형 개체를 대상으로 foreach() 문을 사용할 수 있는 것이다.

이 얘기가 잘 이해되지 않으면 Microsoft Visual Basic 6.0에서 지원하는 Collection 개체를 한 번 떠올려보기 바란다. 사실 이 foreach() 문은 Microsoft Visual Basic 6.0의 For Each ... Next 문에서 그 아이디어를 가져온 것으로 생각해도 별 무리가 없을 것 같다. For Each ... Next 문은 Collection 개체가 갖고 있는 모든 Item들에 대해서 루프를 돌면서 작업을 수행하는데, 이를 가능케 해주는 것이 바로 Collection 개체 내부에 숨겨진 프로퍼티인 _NewEnum 프로퍼티다.

좀 더 구체적으로 말하자면 For Each ... Next 문은 _NewEnum 프로퍼티를 통해서 각 Item들의 IUnknown 인터페이스를 얻고 있는 것인데 .NET 환경에서는 IEnumerable 인터페이스가 바로 이 _NewEnum 프로퍼티의 역활을 수행한다. 따라서, 어떤 개체가 foreach() 문에 사용될 수 있는지 없는지를 알고 싶다면 IEnumerable 인터페이스의 구현 유무를 확인하면 된다. 이를 역으로 해석하자면 여러분이 직접 구현한 개체에서 foreach() 문을 지원하고자 한다면 반드시 IEnumerable 인터페이스를 구현해야만 한다는 뜻이 된다.

이제 다음 코드와 같이 foreach() 문을 사용해서 각각의 가상 웹 서버의 정보를 얻어내기만 하면 된다. 그러나, 사소한 문제가 한 가지 있는데 IIsWebService의 하위에는 IIsWebServer 개체들만 존재하는 것이 아니라는 점이다. IIsWebService 개체의 하위에는 IIsWebServer 개체 외에도 IIsFilters 개체와 IIsWebInfo 개체도 존재하기 때문에 무조건 루프를 돌려버리면 오류가 발생한다.

...
// 루프를 돌면서 IIsWebService 의 자식 개체가 가상 웹 서버인 
// 경우에만 리스트뷰에 추가한다.
foreach (DirectoryEntry ObjDE in Elements)
{
    if (ObjDE.SchemaClassName.ToLower() == "iiswebserver")
    {
        ...
    }
}
...

따라서, 이 코드에서처럼 IIsWebServer 개체인 경우에만 필요한 작업을 수행해야 한다. 이제 정말로 필요한 작업을 수행할 수 있다. 다음 코드가 실제 foreach() 문의 전체 코드이다.

...
// 루프를 돌면서 IIsWebService 의 자식 개체가 가상 웹 서버인 
// 경우에만 리스트뷰에 추가한다.
foreach (DirectoryEntry ObjDE in Elements)
{
    if (ObjDE.SchemaClassName.ToLower() == "iiswebserver")
    {
        // 현재 가상 웹 서버의 IIsWebServer 개체를 얻는다.
        DirectoryEntry IISWebServer = 
            new DirectoryEntry("IIS://LocalHost/W3SVC/" + ObjDE.Name);

        // 리스트뷰에 가상 웹 서버의 정보를 추가한다.
        ListViewItem NewItem = new ListViewItem(ObjDE.Name, 0);
        NewItem.SubItems.Add(IISWebServer.Properties["ServerComment"].Value.ToString());
        NewItem.SubItems.Add(IISWebServer.Path);
        NewItem.SubItems.Add(GetStatusDesc(IISWebServer.Properties["ServerState"].Value));
        NewItem.SubItems.Add(IISWebServer.Properties["DefaultDoc"].Value.ToString());
        lvIISSever.Items.Add(NewItem);
    }
}
...

그런데, 이 코드를 가만히 살펴보면 필자가 쓸모없는 작업을 한 번 더 수행하고 있다는 사실을 쉽게 알 수 있을 것이다. 무슨 얘기인가 하면 각각의 가상 웹 서버에 대한 ADsPath를 구성해서 DirectoryEntry 클래스형 개채 변수인 IISWebServer에 다시 한 번 가상 웹 서버 개체를 할당하고 있는 것이다. 어짜피 이렇게 얻은 IISWebServer 개체 변수나 ObjDE 개체 변수는 본질적으로 동일한 가상 웹 서버 개체를 참조하고 있으므로 굳이 새로 변수를 할당하면서까지 새로 가상 웹 서버 개체를 얻을 필요는 없음에도 불구하고 말이다.

그러나, 필자로서는 이번 기회에 ADsPath에 대하여 다시 한 번 확실하게 설명하고 넘어가고 싶었기 때문에 굳이 이렇게 한 단계를 더 거치도록 프로그램을 작성했다. 실제로는 ObjDE 개체를 바로 사용해도 이상없다. 여기에서 ObjDE 개체의 Name 프로퍼티가 가지고 있는 값은 '1', '2', '3' ... 'n' 과 같은 문자열 형식의 숫자값인데 이 값은 Index를 의미한다. Index 자체가 의미하는 바가 무엇인지는 이미 수 차례 설명했으므로 더 이상 설명하지 않는다. 이 Index 값을 사용해서 가상 웹 서버의 ADsPath를 구성하면 결국 다음과 같이 된다.

ADsPath → IIS://LocalHost/W3SVC/1
ADsPath → [IIS Provider]://[Computer]/[IIsWebService]/[Index]

이 ADsPath가 의미하는 바는 명확하다. 바로 Index가 1인, 아마도 기본 웹 사이트일 확율이 높을 가상 웹 서버 개체를 가리키고 있는 것이다. 이제 여러분들은 Index 값을 알기만 하면 그 가상 웹 서버의 개체를 얻어낼 수 있을 것이다.

이번에는 이렇게 얻은 가상 웹 서버 개체에서 실제 정보들을 얻을 순서다. 여기서에는 Index, 가상 웹 서버의 친숙한 이름, ADsPath, 가상 웹 서버의 현재 상태, 그리고 기본 문서 목록 이렇게 다섯 가지 정보를 얻고 있는데, 이 중에서 Index의 값을 구하는 방법은 이미 위의 설명에 나와 있다. 바로 IIsWebServer 개체의 Name 프로퍼티가 Index 값이다. 그리고, ADsPath의 값은 또 다른 프로퍼티, 즉 Path 프로퍼티에서 알아낼 수 있다.

여기에서 한 가지 명확하게 해둬야만 할 사항이 있다. Name 프로퍼티와 Path 프로퍼티는 IIsWebServer 개체의 프로퍼티가 아니다. ADSI 개체에는 모든 개체가 공통적으로 구현해야만 하는 프로퍼티가 모두 여섯 가지가 있는데, 이 여섯 개의 프로퍼티들은 그 개체가 의미하는 바가 무엇인지와는 전혀 상관없이 모든 ADSI 개체에 존재하고 있다. Name 프로퍼티와 Path 프로퍼티는 바로 그 여섯 가지의 공통 프로퍼티에 해당하는 프로퍼티인 것이다.

이 여섯 가지 공통 프로퍼티들에 대한 추가적인 정보에 관해서는 다음의 비교표를 참고하기 바란다. 이 비교표에서는 IIsWebService 개체를 사례로 한 실제값을 보여 주고 있다.

ADSI 프로퍼티 명 DirectoryEntry 클래스 프로퍼티 명 실제값의 예
Name Name W3SVC
ADsPath Path IIS://LocalHost/W3SVC
Class SchemaClassName IIsWebService
GUID Guid 또는 NativeGuid {8B645280-7BA4-11CF-B03D-00AA006E0975}
Parent Parent IIS://LocalHost
Schema (없음) IIS://LocalHost/schema/IisWebService

Name 프로퍼티는 원본이 되는 네임스페이스에서 사용되는 개체의 이름이다. 여기에서 말하는 네임스페이스는, .NET CLR의 네임스페이스가 아니라 좀 더 원론적인 의미에서의 네임스페이스, 즉 디렉터리 시스템 내부에서의 네임스페이스라는 점에 유의하기 바란다. 이 Name 프로퍼티가 중요한 또 한 가지 이유는 바로 이 프로퍼티들의 값을 조합한 문자열이 ADsPath가 된다는 사실 때문이다.

ADsPath 프로퍼티는 그 동안 몇 번이나 설명을 해왔고 실제로 계속 사용해 왔으므로 더 이상 설명할 필요는 없을 것이다. Class 프로퍼티는 해당 ADSI 개체를 정의하는 스키마 클래스(Schema Class)의 이름이다. 이 스키마 클래스라는 것은 해당 개체의 속성과 그 속성의 특성, 즉 필수 속성인지 여부 등을 정의하는 것으로 데이타베이스의 테이블 스키마에 해당한다.

GUID 프로퍼티는 스키마 클래스의 개체에 대한 고유 식별자인데 일반적으로 COM/COM+를 얘기할 때 흔히 거론되곤 하는 바로 그 GUID와 100% 동일한 것이다. ADSI에서 또다시 GUID라는 개념이 사용되는 이유는 COM/COM+의 ProgID와 CLSID와의 관계와 비교해 생각해보면 쉽게 이해할 수 있을 것이다.

의외로 ADSI의 스키마 클래스는 생각보다 쉽게 추가하거나 편집할 수 있다. 그 대표적인 예로 Windows 2000 시스템의 액티브 디렉터리 스키마(Active Directory Schema)를 편집하는 경우를 들 수 있다. 아니면 개인적으로 또는 회사에서 사업상의 목적으로 고유의 디렉터리 시스템을 구축하고 그에 알맞은 ADSI 제공자와 개체들을 개발하여 제공하는 경우도 생각해 볼 수 있을 것이다. 이런 경우 Name 프로퍼티만으로는 유일한 식별자를 얻는 것이 불가능하다. 여러분들 중 한 분과 필자가 동시에 'MyObject'라는 Name 프로퍼티를 가진 ADSI 개체를 개발해서 배포할 수도 있는 노릇이니 말이다. 그래서, 세상에 존재하는 모든 ADSI 개체를 유일하게 구별할 수 있는 방법을 제공하기 위해서 굳이 다시 GUID 개념이 도입된 것이다.

특이하게도 ADSI의 GUID 프로퍼티에 대응하는 DirectoryEntry 클래스의 속성은 제공자에 따라 변하는데, LDAP 제공자의 경우, 즉 액티브 디렉터리(Active Directory)의 제공자인 경우에는 NativeGuid 프로퍼티가 사용되고 그 외의 모든 경우에는 Guid 프로퍼티가 사용된다.

Parent 프로퍼티는 상위 개체, 즉 부모 개체의 ADsPath다. 마지막으로 Schema 프로퍼티는 ADSI 개체의 스키마 클래스 그 자체를 나타내는 ADsPath인데, Schema 프로퍼티에 대응하는 DirectoryEntry 클래스상에는 존재하지 않지만 SchemaEntry 프로퍼티의 Path 프로퍼티를 통해서 간접적으로 값을 얻을 수는 있다. 이 여섯 개의 프로퍼티는 모두 String 형이므로 참고하기 바란다.

이 여섯 개의 프로퍼티들은 매우 중요하다. 이 프로퍼티들에 대한 지식이 없다면 ADSI 프로그래밍 자체가 매우 힘겨워 질 것이다. 반드시 숙지하기 바란다. 이 샘플 프로그램에서는 이 중에서 Name 프로퍼티와 ADsPath(Path) 프로퍼티, 그리고 Class(SchemaClassName) 프로퍼티가 사용되었다.

이제 가상 웹 서버 개체 자체의 프로퍼티에 관해서 살펴보자. 이 프로그램에서 사용되는 가상 웹 서버 개체의 프로퍼티, 즉 IIsWebServer 개체의 프로퍼티는 ServerComment 프로퍼티와 ServerState 프로퍼티, 그리고 DefaultDoc 프로퍼티 이렇게 세 가지 뿐이다. 물론 이 말은 샘플 프로그램에서 사용되는 프로퍼티가 이 세 가지 뿐이라는 말이지 이 세 가지 프로퍼티가 IIsWebServer 개체가 가진 프로퍼티의 전부라는 말은 아니다. 당연히 실제로는 더 많은 수의 프로퍼티가 존재하고 있다.

그렇다면 그런 다양한 프로퍼티들에 관한 정보는 어디에서 얻을 수 있는 것일까? 그것은 순전히 해당 디렉터리 시스템의 ADSI 제공자(즉, 이 경우엔 IIS 제공자가 될 것이다)에서 그에 관한 정보들을 어떤 방식으로 제공하느냐에 따라 좌우된다. 사실 이는 SQL 구문의 올바른 문법을 DBMS에 기준해서 찾아야지 ODBC를 기준으로 찾을 수는 없는 이치와도 같이 너무나도 당연한 것이다. 재미있는 것은 IIS의 경우 이런 정보들이 별도의 문서로 작성되어 따로 제공되는 것이 아니라, IIS 그 자체에 HTML 도움말 형식의 가상 디렉터리로 준비되어 있다는 것이다.

가령 Windows 2000 제품군의 경우, IIS가 설치된 머신에서 Internet Explorer를 실행하고 URL 입력란에 http://LocalHost/iishelp/를 입력하면 IIS를 설치할 때 함께 설치되는 IIS 5.0 설명서가 출력되는데, 이 IIS 5.0 설명서의 좌측 트리뷰에서 Active Server Pages 가이드IIS를 프로그래밍 방식으로 관리프로그래밍 방식 관리 참조IIS 관리자 개체 참조의 순서대로 클릭하면 IIS에서 제공하는 모든 ADSI 개체들에 대한 참조를 찾아볼 수 있다. IIsWebServer 개체의 다른 프로퍼티나 다른 개체들의 프로퍼티에 관한 정보는 이 곳을 참고하기 바란다.

이 샘플 프로그램에서 IIsWebServer 개체의 ServerComment 프로퍼티의 값을 가져오는 실제 부분을 살펴보면 다음 코드와 같다. 이 코드에서는 ServerComment 프로퍼티의 값을 얻고, 그 값을 리스트뷰 컨트롤에 설정하기 위해서 생성한 새 ListViewItem 클래스형 개체에 그 값을 설정하고 있다.

...
NewItem.SubItems.Add(IISWebServer.Properties["ServerComment"].Value.ToString());
...

단 한 번이라도 기존 방식으로 ADSI 프로그래밍을 해 본 경험이 있는 분이라면 이 코드를 보고 확실히 알 수 있을 것이다. 그렇다. 바로 이 부분이 .NET에서 가장 많이 바뀐 부분이다. 기존 방식에서는 특정 프로퍼티의 값을 가져오기 위해서 다음과 같이 IADs 인터페이스의 Get() 메서드나 GetEx() 메서드를 사용해야만 했었다.

...
var_A = NewItem.Get("ServerComment")        // 또는... var_A = NewItem.ServerComment
var_B = NewItem.GetEx("otherHomePhone")     // 또는... var_A = NewItem.otherHomePhone
...

그러나, 앞의 코드에서 볼 수 있는 것처럼 .NET 환경의 ADSI 프로그래밍에서는 ADSI 개체의 모든 프로퍼티값을 DirectoryEntry 클래스의 Properties 프로퍼티를 통해서만 얻을 수 있다. PropertyCollection 클래스형 개체인 이 Properties 프로퍼티 역시 IEnumerable 인터페이스를 구현하고 있으므로 foreach() 문에서 사용이 가능하다. 그에 더해서 IDictionary 인터페이스 또한 구현하고 있으므로 코드에서 볼 수 있는 것처럼 키를 사용해서 값에 접근하는 것이 가능하다.

이 코드에서 알아낼 수 있는 또 한 가지 중요한 사실은 과거 Visual Basic 계열의 프로그래밍 언어들과는 다르게 Properties 프로퍼티에서 키를 사용해서 각각의 Item들의 값을 얻어낼 때 Value 프로퍼티를 생략할 수 없다는 점이다. 필자가 만나본 몇몇 초보자분들 중에는 심지어 RecordSet 개체에 Fields Collection이나 Filed 개체에 Value 프로퍼티가 존재한다는 사실 자체조차도 모르시는 분들도 많이 있었는데, 만약 Microsoft Visual Basic 6.0이나 ASP에서 Recordset 개체를 사용할 때처럼 습관적으로 Value 프로퍼티를 생략한다면 곧바로 오류가 발생하게 될 것이다.

이 샘플 프로그램의 코드 중에서 필자가 지금까지 설명하지 않은 부분들 중 대부분은 자동으로 생성된 코드들이다. 그 외에 설명하지 않은 부분들 중 필자가 직접 추가한 부분으로는 리스트뷰 컨트롤과 관련된 코드 부분이 있는데 이 글에서는 이에 관해 특별히 설명하거나 하지는 않을 생각이다. 리스트뷰 컨트롤과 관련된 코드들에 관해서는 직접 .NET 문서나 기타 관련 서적을 참고하기 바란다.

그 밖에도 GetStatusDesc()라는 메서드를 사용하고 있는 부분이 있는데, 이 메서드는 단지 숫자값으로 리턴되는 ServerState 프로퍼티의 값을 이해하기 쉬운 문자열 형태로 바꿔주는 역활을 하는 단순한 유틸리티용 메서드이다. 이 메서드 역시 따로 설명하거나 하지는 않겠다. 왜냐하면 그저 한 번 읽어보는 것만으로도 충분히 그 내용을 파악할 수 있기 때문이다.

마지막으로 지금까지 이야기한 내용들을 정리해보도록 하겠다. .NET 환경에서 변경된 ADSI 프로그래밍의 특징은 다음과 같이 두 가지로 요약될 수 있다.

  1. IADs 인터페이스와 IADsContainer 인터페이스 대신 DirectoryEntry 클래스와 DirectoryEntries 클래스가 사용된다.
  2. IADs.Get() 메서드와 IADs.GetEx() 메서드 대신 DirectoryEntry.Properties 프로퍼티가 사용된다.

물론, 이 두 가지 특징이 .NET 환경에서 변경된 ADSI 프로그래밍의 특징에 관한 모든 것은 아니다. 단지 이 글에서 설명한 내용을 요약한 것일뿐, 실제로는 이 밖에도 많은 이슈들이 존재한다.

몇 가지 예를 들어보자면 현재 시스템에 로그인한 사용자 계정의 권한 대신 다른 사용자 계정의 권한을 사용해서 ADSI를 사용하는 방법, 프로퍼티 캐쉬를 고려해서 특정 프로퍼티의 값만 얻거나 변경하는 방법, 디렉터리 시스템의 특정 트리 하위에서 임의의 조건을 만족하는 개체들을 검색하는 방법, 새로운 개체를 생성하거나 삭제하는 방법 등이 그것이다. 다음글에서는 이런 주제들에 관해서 하나씩 차분하게 얘기해보도록 하겠다.