Introduction

이번시간에는 "찌니남편"님께서 방명록을 통해 질문해주신 내용대로, 네트워크 상의 컴퓨터 목록을 가져오는 방법에 대해 소개합니다. 윈도우를 제어하는 작업이다보니 이를 구현하는 방법은 다양하지만 이번시간에는 간단히 .Net Framework상에서 제공하는 기능들을 이용해 네트워크 상에 존재하는 컴퓨터이름, 아이피주소, 연결상태등을 확인하는 방법에 대해 설명드리겠습니다. 아래는 이번시간에 구현할 내용의 결과물 이미지 입니다.

Using Active Directory Services Interfaces

Active Directory Service Interfaces(ADSI)는 여러 네트워크 공급업체들이 제공하는 다양한 디렉터리 서비스의 기능을 이용해 네트워크 자원을 관리하는 디렉터리 서비스 인터페이스 집합으로, 간단하게 네트워크 디렉토리에 접근하고 제어 할 수 있도록 돕습니다. .Net Framework에서도 System.DirectoryServices 네임스페이스를 통해 ADSI를 사용할 수 있기 때문에, 이번시간에는 이를 통해 구현해보도록 하겠습니다.

※ System.DirectoryServices 네임스페이스는 System.DirectoryServices어셈블리를 참조 추가 한 뒤 사용할 수 있습니다.

사용법은 매우 간단합니다. WinNT를 이용해 네트워크영역을 DirectoryEntry를 생성하고 Workgroup을 탐색해가며 네트워크 상에 연결된 컴퓨터의 목록을 가져올 수 있습니다. 아래는 소스코드입니다.


위 코드를 간단히 설명하면, DirecotryEntry를 이용해 네트워크상의 컴퓨터 이름을 가져온뒤 DNS를 이용해 해당 컴퓨터의 IP주소 목록을 가져옵니다. 그리고 Ping을 통해 대상 컴퓨터의 상태와 응답속도를 확인합니다. 소스가 크게 복잡하지 않기 때문에 코드에 대한 설명은 여기까지로 하며, 혹시 더 궁금하신 부분이나 문의 사항은 이메일이나 댓글로 남겨주시면 답변드리도록 하겠습니다.

Introduction

C# 에서의 이벤트는 해당 이벤트가 선언된 클래스에서만 호출할 수 있는 특수한 종류의 Multicast Delegate의 형태로서, 아래와 같이 이벤트가 정의된 클래스를 상속 받아 구현 했다 하더라도 이벤트를 직접 호출할 수 는 없습니다.


이를 해결하기 위한 일반적인 방법으로 아래와 같이 부모객체에 해당 이벤트를 호출 할 수 있는 메서드를 미리 정의 함으로서 이벤트를 호출 하는 방법을 사용합니다.


그런데 만약 부모의 소스코드에 접근 할 수 없는 상황(예: 라이브러리에서 제공하는 객체를 상속받아 구현하는 경우)이거나 외부객체의 이벤트를 강제로 호출하고자 할 경우에 여러가지 문제가 발생할 수 있습니다. 이번시간에는 외부 혹은 내부에 정의된 이벤트에 접근하고 해당 이벤트를 직접 호출하는 방법에 대해 소개합니다.

Create RaiseEventHelper

외부에서 정의된 이벤트를 호출하기전에 먼저 개발자가 이벤트를 정의 했을때 내부적으로 어떠한 변화가 있는지를 확인 해볼 필요가 있습니다. 먼저 아래와 같이 간단한 이벤트가 정의된 코드를 빌드한 후 Windows SDK에서 제공하는 IL DASM을 이용해 작성한 코드가 어떻게 변화 했는지 확인 해 보겠습니다.

왼쪽은 이벤트를 정의 하기 전의 어셈블리이며. 오른쪽이 위 코드를 빌드한 어셈블리입니다. 단순히 이벤트한줄만 추가했을 뿐인데 내부적으로 여러개의 멤버들이 추가로 생성된것을 확인 할 수있습니다.

추가된 항목중에 관심있게 살펴봐야 하는 것은 DummyEvent 필드로, 코드에서 정의한 이벤트와 같은 Delegate를 사용하고 이름까지 동일하는 것으로 보아 이벤트를 구독하거나 해지 할때 DummyEvent필드를 이용한다는것을 알 수 있습니다. 이제 이벤트가 생성되었을때 내부적으로 관리되는 Delegate가 어떻게 생성되는지 알았으므로 Reflection을 이용해 외부에서도 얼마든지 이벤트를 호출 할 수 있습니다.


간단히 코드를 설명하면 Target 객체에 EventName에 해당하는 Delegate를 검색하고 이를 구독하는 여러 메서드들을 직접 호출해주는것으로 이벤트를 호출하는것과 동일한 효과를 낼 수 있습니다. 그리고 실제 사용할 때에는 아래와 같이 사용할 수 있습니다.


이상으로 이번 포스팅을 마치며 기타 궁금하신점이나 다른 문의사항은 메일이나 댓글로 남겨주시면 답변드리도록 하겠습니다. 감사합니다.
 

Introduction

.Net Framework를 이용해 COM객체를 사용하다보면, Object 형식으로 객체가 리턴되는 경우가 있는데 이 객체를 활용하기 위해서는 적절한 Type으로 캐스팅이 필요한 상황이 됩니다. 다행히 Object객체가 어떤 Type의 객체인지 알고 있는 상황이라면 간단하게 캐스팅문법을 이용해 캐스팅 해서 사용할 수 있지만, Type을 모르고 있다면 문제가 발생합니다.

일반적인 .Net Framework환경에서 였다면 간단히 GetType메서드를 통해 해당 객체의 Type을 확인 할 수 있지만, COM 객체의 경우 GetType메서드를 호출하게 되면 System.__ComObject 를 리턴하기 때문에 확인 할 수가 없습니다.

Microsoft KB문서(http://support.microsoft.com/kb/320523)에 보면 as 연산자를 이용해 하나씩 캐스팅을 할수 있다고 하지만, 이렇게 할경우 Type이 많아질 수록 더 많은 체크 루틴을 작성해야 하기 때문에 적합한 방법은 아닌것 같습니다.

이번시간에 소개할 내용은 이러한 상황에서 COM객체의 Type을 확인 할 수 있는 방법에대해 소개합니다.

GetTypeFromComObject Method

COM 객체의 Type을 가져오기 위해서 몇 가지 COM객체의 특성을 이용합니다. 첫째로 COM객체는 GetType메서드를 호출하면 System.__ComObject타입을 리턴한다는 점과, 둘째로 COM Interop Type은 Interface이고, 고유의 GUID를 갖는다는 점을 이용합니다. 소스코드에 대한 설명은 주석으로 대체합니다.



위 코드에서는 Type을 확인 하기 위한 객체와, 해당 COM객체의 Interop Assembly를 입력해 Assembly에 정의된 Type들 중 QueryInterface가 가능한 객체를 검색하는 과정을 수행합니다.
 
 

Introduction

Windows용 소프트웨어를 개발하다보면 사용자 로그를 기록하거나, 특별한 권한이 필요한 기능을 수행하려고 할때, 현재 사용자의 권한을 알아야 하는 상황이 있습니다. 이번시간에 소개할 내용은 .Net Framework를 이용해 사용자 권한의 가져오는 방법에 대해 설명합니다.



 

 

Introduction

Application을 개발할때 목록에 항목이 추가되거나, 제거 되었을때와 같은 동적인 변경내용을 수신기에 알리기 위해 일반적으로 목록객체에 INotifyCollectionChanged를 상속받아 구현하거나  INotifyCollectionChanged가 구현된 ObservableCollection<T>등을 사용합니다. 동적인 목록의 변경에 대하여 즉각 반응하기 때문에 매우 유용하게 사용될 수 있지만, 많은 양의 목록이 추가/제거 될경우 매번 목록의 변동이 있을대마다 CollectionChanged이벤트가 발생하기 때문에 퍼포먼스에 크게 영향을 미칠 수 있습니다. 이번시간에는 많은 양의 아이템을 INotifyCollectionChanged가 구현된 목록에 추가/제거할때 CollectionChanged가 발생하는것을 방지하고 처리가 모두 끝난뒤에 호출되도록 하는 방법에 대해 소개합니다.

List<T> VS ObservableCollection<T>

위 동영상은 INotifyCollectionChanged가 정의되지 않은 List<T>와 INotifyCollectionChanged가 정의된 ObservableCollection<T>를 이용해 ListBox의 Item목록을 변경 했을때의 퍼모먼스 비교입니다. 데모에서는 동일한 갯수의 목록(30만 건)을 목록에 추가하고 ListBox를 갱신하는 과정까지의 시간을 측정했습니다.

결과는 INotifyCollectionChanged를 정의하지 않은 List<T>가 INotifyCollectionChanged가 정의된 ObservableCollection<T>보다 10배정도 빠른 결과를 나타내고 있습니다. 이유는 ObservableCollection에서 항목이 추가될때마다 CollectionChanged이벤트가 발생하고 이를 수신하는 ListBox에서는 매 항목이 추가될때마다 View를 갱신하고 있지만, List<T>는 항목이 추가되더라도 View를 갱신하지 않기 때문입니다. List<T>는 View에 영향을 미치지않기 때문에 목록추가가 완료된뒤 View의 ItemsControl에 Refresh메서드를 호출하는것으로 View를 갱신할 수 있습니다.

하지만, List<T>를 이용했을때가 항상 성능이 뛰어난 것은 아닙니다. 위에서 언급했듯이 List<T>는 INotifyCollectionChanged가 구현되지 않았기 때문에 목록에 아이템을 추가할때마다 직접 View의 ItemsControl을 Refresh 메서드를 호출해줘야하는데, ItemsControl의 Refresh메서드를 호출하게 되면 View를 다시 그리기 때문에 이는 더큰 부작용을 일으킬수 있습니다. 

Create SuspendObservableCollection

가장 좋은 방법은 작은 건의 항목이 변경될때에는 INotifyCollectionChanged를 이용해 해당 항목이 추가/제거 되었음을 알리고, 많은 항목이 변경될때에는 CollectionChanged이벤트 발생을 일시적으로 중단하고, 모든 항목의 변경이 완료되었을때 CollectionChanged를 발생시키는 방법입니다. 하지만, ObservableCollection의 경우 CollectionChanged 이벤트에 대한 중지여부를 직접적으로는 제어 할 수 없기 때문에 ObservableCollection을 상속받아 CollectionChanged이벤트 발생여부를 제어 할 수 있는 SuspendObserableCollection을 구현할 수 있습니다. 아래는 SuspendObservableCollection의 소스코드입니다.


ObservableCollection의 OnCollectionChanged에서 내부적으로 발생시키는 CollectionChanged이벤트 호출을 제어하기 위해 IsSuspend 속성을 구현해 CollectionChanged 이벤트의 호출을 제어 했습니다. 실제 적용될 때에는 다음과 같이 사용할 수 있습니다.


이제 한두건 정도의 간단한 목록 변경에서는 INotifyCollectionChanged를 이용해 변경을 알리고 많은건의 목록변경에서는 IsSuspend와 Refresh메서드를 이용해 CollectionChanged이벤트를 제어 할 수 있습니다. 아래 동영상은 위에서 실시한 퍼포먼스 비교를SuspendObservalbleCollection에도 적용한 내용입니다.

Reflection for INotifyCollectionChanged

추가로, Reflection을 이용해 SuspendObservableCollection을 구현하지 않고 CollectionChnaged이벤트에 대한 제어를 수행할수 있습니다. 아래 코드는 Reflection을 이용한 CollectionChanged이벤트 제어를  Extension Method로 구현한 코드는 입니다.


CollectionChanged를 제어하기 위해서 PasueNotifyCollectionChanged메서드와 ResumeNotifyCollectionChaged를 이용합니다. 퍼포먼스는 SuspendObservableCollection보다 다소 느리지만, Type을 수정할 수 없는 상황이거나, INotifyCollectionChanged을 구현한 다른 Type의 Collection에서 유용하게 사용할 수 있습니다.