그 동안 3회에 걸쳐 COM 컴포넌트를 클래스 라이브러리나 기타 개발 도구의 힘을 빌리지 않고 C++만을 이용해 만들어 보았다. 아마 많은
독자 여러분께는 상당히 난해한 내용이었으리라 생각된다. 이번 회에서는 앞서 만들어 보았던 COM 컴포넌트를 비주얼 C++와 함께 제공되는
ATL(Active Template Library)을 이용하여 다시 만들어 보도록 하겠다. 이 과정을 통해 COM 개발 도구를 이용하면 개발에
들어가는 시간과 노력이 얼마나 단축될 수 있는지 알 수 있을 것이다. 단 이 글에서는 지면 관계상 ATL에 대해 별도의 설명을 하지는 않겠다.
매회 하다시피 한 이야기이지만 실제로 COM 컴포넌트를 개발할 일이 있을 경우에는 본 예제에서처럼 ATL이나 MFC나 VB나 델파이와 같은
컴포넌트 개발 도구를 사용하면 된다.

——————————————————————————–
참고
ATL이란
ATL은 비주얼 C++에 딸려오는 C++ 템플릿 클래스 라이브러리로 작고 빠른 COM 컴포넌트를 만들기 위한 목적으로 만들어
졌다. 이를 이용하면 ActiveX 컨트롤이나 코드 컴포넌트, IIS(Internet Information Server)상에서
ASP(Active Server Page) 프로그래밍을 할 때 사용할 수 있는 서버 컴포넌트 등을 만들 수 있다. 다른 COM 개발 도구와
비교하자면 속도나 크기에는 잇점이 있지만 프로그래밍이 좀 난해하고(C++의 템플릿이란 것 자체가 제대로 쓰려면 좀 시간이 걸린다) COM에 대한
상당한 지식을 필요로 한다는 점이 단점이라고 할 수 있다.

——————————————————————————–

1. ATL을 이용한 COM 컴포넌트 작성
지난 3회에 걸친 연재에서는 COM 컴포넌트를 처음부터 직접 만들었다. COM 컴포넌트가
지원해야 하는 4개의 함수들 (DllRegisterServer, DllUnRegisterServer, DllGetClassObject,
DllCanUnloadNow)뿐만 아니라 IUnknown, IClassFactory 인터페이스를 직접 구현해야 했고 컴포넌트를 VB와 같은
RAD(Rapid Application Development) 환경하에서 사용가능하도록 해주는 IDispatch 인터페이스도 모두 직접 구현해야
했다. 사실 COM 컴포넌트를 만듬에 있어 가장 중요한 것은 구현하고자 하는 인터페이스 자체이다. 지난 연재에서 만들어본
IMyInterface라는 인터페이스에서는 그 구성 요소인 DisplayOSType과 DisplayMemorySize라는 멤버 함수를 만드는
것이 가장 중요한 일이다. 그럼에도 불구하고 이것들 자체를 만드는 것보다는 그 기본이 되는 뼈대를 만드는데 더 많은 시간과 노력을 들여야만
했다.

ATL을 이용해서 COM 컴포넌트를 만들게 되면 자신이 만들고자 하는 대상이 되는 인터페이스만 자체에만 주력하면 된다. 나머지는 모두
ATL 위저드가 만들어 준다. 앞서 이야기했던 4개의 전역 함수들과 IUnknown, IClassFactory, IDispatch와 같은
인터페이스를 모두 ATL 위저드에서 구현해주고 이 바탕 위에서는 개발자는 자신이 만들고자 하는 인터페이스 자체에만 주력하면 된다.

IDL(Interface Definition Language)
CORBA 프로그래밍을 해본 경험이 있거나 유닉스에서
RPC(Remote Procedure Call) 프로그래밍을 한 경험이 있는 사람이라면 IDL이라는 것을 알 것이다. 정식(?)으로 하는 COM
프로그래밍에서도 IDL을 위의 경우에서와 아주 흡사하게 사용한다. IDL은 인터페이스 자체를 정의하기 위한 텍스트 기반의 언어로 이를 이용해
만들고자 하는 컴포넌트의 이름과 인터페이스의 종류와 그를 구성하는 멤버 함수들의 원형(Prototype)을 기술하는데 사용한다. 즉 IDL은
외부에서 컴포넌트를 볼 때의 모양만 적는 언어이지 구현 자체에 사용되는 언어는 아니다. IDL 파일은 컴포넌트마다 하나씩 존재하게 된다. 비주얼
C++에는 MIDL(Microsoft Interface Definition Language) 컴파일러라는 것이 있어서 이것으로 IDL 파일을
컴파일하면 이를 C++클래스와 구현 파일로 변환해준다. 개발자는 널 함수로 구현되는 있는 구현 파일의 내부만 채워주면 된다. ATL 프로그래밍도
기본은 이 것과 동일하다. 즉 IDL을 이용해서 COM 컴포넌트를 기술하고 IDL 파일을 컴파일하면 .H 파일과 .CPP 파일이 만들어지고 그
내부만 채우는 형식이 되는 것이다. 그 과정에서 앞서 말한 복잡하고 귀찮은 작업들은 ATL 위저드가 대신 수행해준다.

위의 설명을 통해 알 수 있겠지만 ATL로 COM 프로그래밍을 제대로 할 수 있으려면 IDL에 대해 좀 알면 좋다. ATL의 New ATL
Object와 기타 보조 명령들을 이용하면 다이얼로그만을 통해서 IDL 파일을 조작할 수 있기 때문에 처음에는 IDL에 대해 잘 몰라도
관계없지만 시간이 지나면 가끔은 IDL 파일을 직접 조작해야할 일들도 생길 것이다. 앞서 ATL로 하는 COM 프로그래밍은 좀 어렵다고 했는데
그 이유 중의 하나가 IDL 파일을 알아야 하고 이를 다뤄야할 경우가 생기기 때문이다. VB나 델파이로 COM 컴포넌트를 만드는 경우에는
IDL이란 것의 존재 자체를 아예 모르고 프로그래밍을 할 수 있다. 그만큼 프로그래밍은 쉽지만 세밀하게 다룰 수는 없다는 이야기도 되긴 될
것이다. IDL 파일이 어떻게 생겼는지는 뒤에서 예제를 실제로 만들어볼 때 보도록 하자.

——————————————————————————–
참고
IDL과 타입 라이브러리
전 회에서 타입 라이브러리에 대해 언급한 바 있다. 타입 라이브러리는 어떤 COM 컴포넌트에 어떤 메소드와
프로퍼티가 존재하는지 적혀 있는 파일이라고 하였다. 기존의 DLL에 헤더 파일이 존재하는 것과 비슷한 용도라고 생각하면 된다. IDL로 기술된
COM 컴포넌트는 컴파일하면 결과 파일로 타입 라이브러리가 같이 생성되며 이는 VB와 같은 RAD 툴에서 선지정(Early-binding)
방식으로 컴포넌트를 사용할 때 반드시 필요하다.

——————————————————————————–

2. 예제 컴포넌트의 구현
그럼 이제 지난 회까지 만들어 보았던 IMyInterface라는 인터페이스를 갖는 COM 컴포넌트를
ATL로 만들어 보도록 하자. 이 인터페이스에는 다음과 같은 멤버 함수가 존재하였다.

DisplayOSType();
DisplayMemorySize();

프로젝트 생성
먼저 비주얼 C++를 실행하고 File 메뉴의 New 명령을 띄운다. Projects 탭을 선택하고 첫 번째 항목인
ATL COM AppWizard를 선택한다. 우측의 Project name 박스에는 프로젝트 이름을 MyCom이라고 입력한다. 그 아래의
Locations 박스에는 프로젝트가 놓일 디렉토리를 적당히 선택한다. 그러면 다음과 같은 프로젝트 설정 다이얼로그가 뜰 것이다.

&lt 그림 1. ATL COM AppWizard Step 1 of 1 다이얼로그 &gt
Server
Type으로는 Dynamic Link Library를 선택하고 하단의 체크 박스 중에서 아무 것도 선택하지 않는다. MFC 프로그래밍을 할 줄
안다면 Support MFC 박스(ATL에서는 기본적으로 MFC는 사용할 수 없고 ATL에서 제공해주는 라이브러리와 API만을 사용할 수
있다)는 선택하면 좋다. 또 만드는 컴포넌트가 서버 컴포넌트이고 이를 MTS(Microsoft Transaction Server) 위에서
동작시킬 일이 있다면 Support MTS 박스도 선택하는 것이 좋다. Finish 버튼을 누른다.

인터페이스의 추가
이제 여기에 우리가 원하는 IMyInterface를 추가하는 작업을 해야 한다. Insert 메뉴의 New ATL
Object 명령을 선택한다. 그림 2와 같은 다이얼로그가 뜰 텐데 지금 만들고자 하는 컴포넌트는 Category는 Objects에 해당하고
우측에서는 Simple Object에 해당한다.

&lt 그림 2. Simple Object의 선택 &gt
Next 버튼을 누르면 그림 3과 같은 다이얼로그가 뜰
것이다. C++ 박스의 Short Name 박스에 MyInterface라고 입력한다. 그러면 다른 비어있던 박스들도 모두 내용이 채워질 것이다.
우측의 COM 박스 하단의 Prog ID 박스의 내용만 이전 예제에서와 동일하게 MyCom.Interface로 수정한다.

&lt 그림 3. ATL Object Wizard 등록 정보 다이얼로그 – Names 탭 &gt

다이얼로그에서 Attributes 탭을 선택하면 그러면 그림 4와 같은 모양이 될 텐데 거기서 중간 Interface 박스의 선택으로 Dual이
되었음을 확인하기 바란다. 이 선택만으로 여기서 추가하는 인터페이스에는 자동으로 IDispatch 인터페이스가 구현된다. 와우 ! 지난 회에서
IDispatch를 구현하기 위해 들였던 노력을 생각해보면 허무하기까지 할 것이다.

&lt 그림 4. ATL Object Wizard 등록 정보 다이얼로그 – Attributes 탭 &gt
확인
버튼을 누른다. 그러면 앞서 이야기했던 4개의 함수들 (DllRegisterServer, DllUnRegisterServer,
DllGetClassObject, DllCanUnloadNow), IUnknown, IClassFactory, IDispatch 인터페이스를
다시 구현할 필요가 없다. 이 얼마나 편리한 일인가 ? 4개의 전역 함수들의 경우에는 MyCom.cpp를 에디터로 올려서 보면 이미 구현되어
있다는 것을 알 수 있을 것이다.

STDAPI DllCanUnloadNow(void)
{

}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{


}

STDAPI DllRegisterServer(void)
{

}

STDAPI DllUnregisterServer(void)
{

}

그리고 프로젝트 웍스페이스의 클래스 뷰를 보면 CMyInterface라는 클래스가 만들어져 있을텐데 그것이 바로 인터페이스가 정의되는
클래스가 된다. 또 그 밑에는 IMyInferface라는 것이 보일텐데 이것은 IDL 파일내의 IMyInterface 정의를 가리킨다.

메소드의 추가
이제 IMyInterface 인터페이스에 DisplayOSType과 DisplayMemorySize 메소드를
추가해보도록 하자. 프로젝트 웍스페이스의 클래스뷰에서 IMyInterface를 오른쪽 클릭하고 Add Method 명령을 선택한다. 그러면
메소드를 추가해주는 그림 5와 같은 다이얼로그가 하나 뜰 것이다. 거기서 Method Name 박스에 DisplayOSType이라고 입력한다.
인자가 없기 때문에 그 상태에서 OK 버튼을 누른다.

&lt 그림 5. 메소드의 추가 &gt
위의 과정을 반복하여 DisplayMemorySize 메소드를 추가한다.
이렇게 하면 IDL 파일에 이 메소드들이 자동으로 등록되고 해당 클래스에 멤버 함수로 등록된다. 그러면 IDL 파일의 내용을 한번 보도록 하자.
MyCom.idl이라는 파일이 있을 텐데 그 내용을 보면 IMyInterface라는 인터페이스가 정의된 부분이 있을 것이다.

interface IMyInterface : IDispatch
{
[id(1), helpstring(“method
DisplayOSType”)] HRESULT DisplayOSType();
[id(2), helpstring(“method
DisplayMemorySize”)]
HRESULT DisplayMemorySize();
};

이 것으로 컴포넌트 뼈대는 다 만들었다. 새로 만든 두 개의 메소드 내부만 구현하면 된다. 이는 MyInterface.cpp에 비어있는
함수로 정의되어 있다.

STDMETHODIMP CMyInterface::DisplayOSType()
{
// TODO: Add your
implementation code here
return S_OK;
}

STDMETHODIMP CMyInterface::DisplayMemorySize()
{
// TODO: Add your
implementation code here
return S_OK;
}

위의 내용을 적당히 채워주면 컴포넌트는 다 만든 것이다. 전 회의 예에서처럼 아주 간단하게 테스트용으로 메시지 박스 하나를 덜렁 띄워버려도
된다. 지금까지의 과정을 잘 뒤돌아 보면 개발자가 코드를 만드는 것은 추가한 인터페이스의 메소드에 해당하는 함수의 내부를 채우는 경우뿐이다. 그
전단계까지는 ATL과 비주얼 C++에서 제공해주는 툴을 이용해 잘 선택(?)하기만 하면 된다. C++로 처음부터 작업하는 것보다 훨씬 더
편리하다는 것을 알 수 있을 것이다. 컴파일을 해보면 별 이상 없이 컴포넌트가 하나 생성되고 이 것이 레지스트리에 등록될 것이다.

3. 예제 컴포넌트의 사용
그럼 이렇게 만든 컴포넌트를 VB에서 사용해보도록 하자. 지난 회에서 만들어본 예제 컴포넌트는 IDL을
사용하지 않았기 때문에 타입 라이브러리를 지원하지 못했고 그렇기 때문에 VB에서 선지정 방식으로 사용할 수 없었다. 하지만 이번에 ATL로 만든
컴포넌트는 IDL을 사용하기 때문에 타입 라이브러리가 만들어지고 따라서 VB에서 선지정 방식으로 사용할 수 있다. 먼저 후지정 방식으로 사용하는
예(지난 회에서의 예)는 다음과 같았다.

Dim comMy As Object

Set comMy = CreateObject(“MyCom.Interface”)
comMy.DisplayMemorySize

comMy.DisplayOSType
Set comMy = Nothing

첫 번째 줄에서 comMy라는 이름의 컴포넌트가 어떤 타입인지 정확히 지정할 수 없었다. 따라서 실행되어서 실제로 컴포넌트의 메소드를
부르기 전까지는 존재하는 메소드를 호출하는 것인지를 검사할 수조차도 없었다. 이걸 선지정 방식으로 변경해보자. 비주얼 베이직을 실행한다.
프로젝트 타입으로 Standard EXE를 선택한다. 다음으로 할 일은 IMyInterface가 정의되어 있는 타입 라이브러리를 VB에 알려
주어서 그 인터페이스가 어떤 메소드와 프로퍼티를 갖는지 알려주는 것이다. 그래야만 선지정 방식으로 컴파일되어 동작할 수 있다. 프로젝트 메뉴에서
참조 명령을 선택한다. 그러면 이 시스템에 존재하는 모든 타입 라이브러리의 목록이 나타난다. 거기서 MyCom 1.0 Type Library라는
항목을 찾을 수 있을 것이다. 이를 체크하고 OK 버튼을 누른다. 이제 폼위에 적당히 버튼을 하나 올리고 그 버튼의 처리 함수를 만든다. 그
내부를 다음과 같이 채운다.

Dim comMy As MYCOMLib.MyInterface

Set comMy = CreateObject(“MyCom.Interface”)
comMy.DisplayMemorySize

comMy.DisplayOSType
Set comMy = Nothing

후지정 방식의 예제에서처럼 comMy의 타입을 범용 객체 타입인 Object로 지정하지 않고 MyCOMLib.MyInterface라는
타입으로 지정하였다. 이렇게 하면 없는 메소드나 메소드의 인자 개수나 타입을 잘못 지정했을 경우 컴파일시 문법 오류가 발생한다.

이 번 회에서는 ATL로 COM 컴포넌트를 만드는 것에 대해 알아보았다. 지난 회와 비교해본다면 COM 개발 도구를 사용하는 것이 얼마나
일을 간단하게 해주는지 느껴볼 수 있었을 것이다.

——————————————————————————–
Copyright
2000ⓒ 한기용  Designed By 한기용

Leave a Reply

Your email address will not be published. Required fields are marked *