148
1 Visual C++ ATL/COM Programming [email protected]

Visual C++ ATL/COM Programming

  • Upload
    filia

  • View
    162

  • Download
    17

Embed Size (px)

DESCRIPTION

Visual C++ ATL/COM Programming. 천 정 아 [email protected]. 교육일정. COM 프로그래밍 개요. Component Object Model. 자신의 고유한 기능을 제공하는 단위 어플리케이션 ( 즉 , 컴포넌트 ) 의 통합 및 커뮤니케이션 방법에 대한 표준을 정의한 사양 COM 컴포넌트를 정의하는 방법에 대한 표준이면서 COM 컴포넌트를 사용하는 방법에 대한 표준임 핵심적임 MS 의 기반 기술 역할 제공 - PowerPoint PPT Presentation

Citation preview

Page 1: Visual C++ ATL/COM Programming

1

Visual C++ ATL/COM Programming

천 정 아[email protected]

Page 2: Visual C++ ATL/COM Programming

2

교육일정

1 일차 COM 프로그래밍 개요

2 일차 Visual C++ COM 컴파일러

3 일차 ATL 개요

4 일차 자동화와 이중 인터페이스

5 일차 이벤트 , 재사용 , 스레딩 모델

Page 3: Visual C++ ATL/COM Programming

3

COM 프로그래밍 개요

Page 4: Visual C++ ATL/COM Programming

4

Component Object Model 자신의 고유한 기능을 제공하는 단위 어플리케이션

( 즉 , 컴포넌트 ) 의 통합 및 커뮤니케이션 방법에 대한 표준을 정의한 사양 COM 컴포넌트를 정의하는 방법에 대한 표준이면서

COM 컴포넌트를 사용하는 방법에 대한 표준임 핵심적임 MS 의 기반 기술 역할 제공

OLE, ActiveX, ADO, OLE DB, Active Directory 모두 COM 을 기반으로 작성

Page 5: Visual C++ ATL/COM Programming

5

COM 의 목표 각 컴포넌트는 서로 다른 언어로 개발될 수 있어야

한다 . 컴포넌트가 설치되고 실행하는 위치에 관계없이

같은 방법으로 컴포넌트를 사용할 수 있어야 한다 . 컴포넌트의 버전 관리가 쉬워야 한다 . 한 업체에 종속적이지 않아야 한다 .

Page 6: Visual C++ ATL/COM Programming

6

COM 컴포넌트의 조건 언어 독립적이어야 한다 . 이진 형태로 제공되어야 한다 . 버전 호환성을 제공해야 한다 . 위치 투명성을 제공해야 한다 .

Page 7: Visual C++ ATL/COM Programming

7

COM 인터페이스 (1) COM 객체가 자신의 기능을 노출시키는 기본적인

방법 COM 객체와 이를 사용하는 클라이언트 사이의

계약

COM컴포넌트COM

컴포넌트COM

클라이언트COM

클라이언트COM

인터페이스

Page 8: Visual C++ ATL/COM Programming

8

COM 인터페이스 (2) 인터페이스의 의미

논리적 의미 특정 서비스를 제공하는 일련의 함수 ( 메서드 ) 들 ~ 하는 기능 ( 서비스 )

물리적 의미 함수 포인터 배열 형태의 메모리 구조 C++ 의 가상함수테이블 순수 가상함수로만 구성되는

추상 클래스로 정의 각 COM 객체는 반드시 IUnknown

인터페이스와 COM 객체 고유의 기능을 노출하는 하나 이상의 인터페이스를 제공

Page 9: Visual C++ ATL/COM Programming

9

인터페이스 메모리 구조interface IFoo : IUnknown{ virtual HRESULT __stdcall Method1() = 0; virtual HRESULT __stdcall Method2() = 0; virtual HRESULT __stdcall Method3() = 0;};class CImplIFoo : public IFoo{ // QueryInterface, AddRef, Release 구현 // Method1, Method2, Method3 구현};

interface IFoo : IUnknown{ virtual HRESULT __stdcall Method1() = 0; virtual HRESULT __stdcall Method2() = 0; virtual HRESULT __stdcall Method3() = 0;};class CImplIFoo : public IFoo{ // QueryInterface, AddRef, Release 구현 // Method1, Method2, Method3 구현};

인터페이스포인터

가상함수테이블QueryInterfaceQueryInterfacevptrvptr

멤버 변수멤버 변수AddRefAddRef

ReleaseRelease

인스턴스

Method1Method1

Method2Method2

Method3Method3

Page 10: Visual C++ ATL/COM Programming

10

COM 클라이언트 / 컴포넌트 COM 컴포넌트 : 자신의 고유한 서비스 제공

COM 서버 : 물리적인 파일 (DLL, EXE) 의 실행 인스턴스

인 - 프로세스 서버 (In-proc Server) : DLL 파일로 구현 아웃 - 오브 - 프로세스 서버 (Out-of-process Server) : EXE

파일로 구현 로컬 서버 (Local Server) 리모트 서버 (Remote Server)

COM 객체 : COM 인터페이스를 구현한 클래스의 인스턴스

COM 클라이언트 : COM 컴포넌트의 서비스를 사용

Page 11: Visual C++ ATL/COM Programming

11

GUID Globally Unique Identifier 128 bit 크기의 정수 값 ( 구조체로 정의 ) 전세계적으로 시간과 장소에 관계없이 고유하다고

보장할 수 있는 값 UUID 에서 유래 GUID 의 사용

IID : 인터페이스 ID ( 인터페이스 식별자 ) CLSID : 클래스 ID (COM 객체 식별자 )

GUIDGEN.EXE 로 생성

Page 12: Visual C++ ATL/COM Programming

12

HRESULT 대부분의 COM 인터페이스 함수는 HRESULT

를 리턴 32 bit 정수값 (LONG) SUCCEEDED, FAILED 매크로와 함께 사용

Return CodeFacility code31 30 16 15 0

Severity code

15 bit 16 bit

Page 13: Visual C++ ATL/COM Programming

13

COM 컴포넌트 사용

Page 14: Visual C++ ATL/COM Programming

14

COM 컴포넌트 등록 레지스트리에 반드시 등록 후 사용

서버 유형인 - 프로세스

서버아웃 - 오브 - 프로세스

서버

레지스트리 등록

Regsvr32 DLL 파일명 EXE 파일명 /RegServer

레지스트리 해제 Regsvr32 /u DLL 파일명 EXE 파일명 /UnregServer

Page 15: Visual C++ ATL/COM Programming

15

COM 클라이언트 작성1. COM 라이브러리 초기화2. COM 객체의 CLSID 구함3. COM 객체의 인스턴스 생성4. COM 객체가 제공하는 인터페이스 포인터를

구하여 메서드 호출 (COM 객체의 서비스 사용 )

5. COM 라이브러리의 초기화를 해제

Page 16: Visual C++ ATL/COM Programming

16

COM 라이브러리 초기화 / 해제 COM 라이브러리

COM 을 사용하는 모든 application 에서 유용하게 사용될 수 있는 컴포넌트 관리 서비스 제공

HRESULT CoInitialize(LPVOID); 프로세스마다 한번만 호출

HRESULT CoInitializeEx(LPVOID, DWORD); #define _WIN32_DCOM 매크로 정의 후 사용 두번째 인자로 COINIT_APARTMENTTHREADED

지정 void CoUninitialize();

CoInitialize 와 짝을 이루어 호출

Page 17: Visual C++ ATL/COM Programming

17

COM 객체의 CLSID 구하기1. CLSID 가 정의된 소스 파일 이용2. 레지스트리 편집기나 OLE/COM

개체뷰어를 사용하여 CLSID 구하기3. ProgID 사용

읽기 쉬운 문자열 형태의 식별자 < 컴포넌트 명 >.< 객체 명 >.< 버전 > HKEY_CLASSES_ROOT 의 서브키로 등록 사용은 간편하지만 유일성 보장되지 않음

4. 형식 라이브러리 이용

Page 18: Visual C++ ATL/COM Programming

18

레지스트리 등록 예HKEY_CLASSES_ROOT

CLSID{22D6F312-B0F6-11D0-94AB-0080C74C7E95}

InProcServer32 : C:\WINNT\System32\msdxm.ocx ProgID : MediaPlayer.MediaPlayer.1 VersionIndependentProgID : MediaPlayer.MediaPlayer

MediaPlayer.MediaPlayer.1 ProgIDCLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95}

MediaPlayer.MediaPlayer VerIndProgIDCurVer : MediaPlayer.MediaPlayer.1CLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95}

Page 19: Visual C++ ATL/COM Programming

19

CLSID 와 ProgID 변환 HRESULT CLSIDFromProgID(LPCOLESTR, LPCL

SID); HRESULT ProgIDFromCLSID(REFCLSID, LPOLE

STR);

Page 20: Visual C++ ATL/COM Programming

20

COM 의 문자열 유니코드 문자열 사용

typedef unsigned short wchar_t; typedef wchar_t WCHAR; typedef WCHAR OLECHAR; typedef OLECHAR* LPOLESTR; typedef const OLECHAR* LPCOLESTR;

TCHAR UNICODE 매크로 정의 시 WCHAR 로 처리 UNICODE 매크로 미정의 시 char 로 처리

Page 21: Visual C++ ATL/COM Programming

21

COM 객체의 생성 STDAPI CoCreateInstance(REFCLSID rclsid, LP

UNKNOWN pUnkOuter, DWORD dwClsContext, REFIID iid, LPVOID* ppv); rclsid : COM 객체의 CLSID pUnkOuter : 통합에서만 사용 dwClsContext : 실행 형태 (CLSCTX_INPROC_SERV

ER, CLSCTX_LOCAL_SERVER 등 ) riid : 사용하고자 하는 인터페이스 IID ppv : COM 객체가 리턴하는 인터페이스 포인터의

주소

Page 22: Visual C++ ATL/COM Programming

22

IUnknown 모든 COM 인터페이스는 IUnknown 을 상속 모든 COM 객체가 갖추어야 할 기본적인

서비스 제공

interface IUnknown{ virtual HRESULT __stdcall QueryInterface( REFIID riid, void** ppv)=0; virtual ULONG __stdcall AddRef()=0; virtual ULONG __stdcall Release()=0;} ;

interface IUnknown{ virtual HRESULT __stdcall QueryInterface( REFIID riid, void** ppv)=0; virtual ULONG __stdcall AddRef()=0; virtual ULONG __stdcall Release()=0;} ;

Page 23: Visual C++ ATL/COM Programming

23

QueryInterface 클라이언트가 COM 객체의 다른 인터페이스를

요청할 때 해당 인터페이스 포인터를 리턴 COM 객체가 제공하는 인터페이스를 구하는

유일한 방법임

Page 24: Visual C++ ATL/COM Programming

24

AddRef/Release COM 객체가 스스로 자신이 몇 번 참조되어

있는가 하는 횟수를 관리 레퍼런스 카운터가 0 일 때 스스로를 소멸 참조 카운터 규칙

1. 인터페이스를 리턴하기 전에 AddRef 한다 .2. 인터페이스의 사용이 끝나면 Release 한다 .3. 인터페이스 포인터를 다른 인터페이스 포인터에

대입할 때도 AddRef 한다 .

Page 25: Visual C++ ATL/COM Programming

25

COM 객체의 사용 인터페이스 포인터를 통하여 인터페이스가

제공하는 메서드 호출 QueryInterface 를 통하여 COM 객체가

지원하는 다른 인터페이스를 요청 인터페이스의 사용이 끝나면 Release 호출 COM 서버가 할당한 메모리를 클라이언트가

해제해야 하는 경우 CoTaskMemAlloc, CoTaskMemFree 사용

Page 26: Visual C++ ATL/COM Programming

26

COM 컴포넌트 구현

Page 27: Visual C++ ATL/COM Programming

27

COM 컴포넌트의 구현1. COM 인터페이스의 정의2. COM 객체 클래스 구현3. 클래스 팩토리 클래스 구현4. COM 서버 구현

Page 28: Visual C++ ATL/COM Programming

28

COM 인터페이스 정의 IDL 로 COM 인터페이스 정의

MIDL 컴파일러로 컴파일 IDL(Interface Definition Language)

인터페이스를 정의하는 표준 개발 도구 MIDL 컴파일러 제공 OSF RPC 의 IDL 을 확장 C/C++ like language with attribute 언어 독립성 제공 ( 형식 라이브러리 ) 위치투명성 제공 ( 프록시 / 스텁 코드 )

Page 29: Visual C++ ATL/COM Programming

29

IDL 의 예

인터페이스 헤더 object : COM 인터페이스 uuid : 인터페이스 식별자

(IID)

// Hello.idl[

object,uuid(B98E4691-4C07-4c4b-8E88-2EC7EEF13862),

]interface IHello : IUnknown{

import “unknwn.idl”;HRESULT sayHello([in, string] wchar_t* name,

[out, string] wchar_t** message);};

// Hello.idl[

object,uuid(B98E4691-4C07-4c4b-8E88-2EC7EEF13862),

]interface IHello : IUnknown{

import “unknwn.idl”;HRESULT sayHello([in, string] wchar_t* name,

[out, string] wchar_t** message);};

메서드의 인자 헤더 in : 클라이언트에서 서버로

이동 ( 마샬링 ) out : 서버에서 클라이언트로

이동 ( 마샬링 ) string : NULL 종료 문자열

Page 30: Visual C++ ATL/COM Programming

30

MIDL 컴파일러의 역할 C/C++ 에서 사용할 수 있는 인터페이스를

정의한 코드를 포함하는 헤더 파일 생성 커스텀 인터페이스에 대한 프록시 (proxy)/

스텁 (stub) 코드 생성 자동화에서 사용되는 형식 라이브러리 (Type L

ibrary) 생성 library, coclass 문이 사용되는 경우에만 생성

Page 31: Visual C++ ATL/COM Programming

31

COM 객체 구현 인터페이스 포함

COM 객체 클래스 안에 인터페이스 구현 클래스를 포함

구문은 복잡하지만 디버깅이 쉽다 . MFC 에서 사용

인터페이스 상속 COM 객체 클래스를 인터페이스에서 상속 간단하다 . ATL 에서 사용

Page 32: Visual C++ ATL/COM Programming

32

COM 객체 클래스 정의

class CHello : public IHello, public IGoodbye{

// IUnknown 메서드 구현// IHello 메서드 구현// IGoodbye 메서드 구현

};

class CHello : public IHello, public IGoodbye{

// IUnknown 메서드 구현// IHello 메서드 구현// IGoodbye 메서드 구현

};

다중 인터페이스 구현을 위해 다중 상속 이용 COM 객체 구현 클래스에서는 모든

인터페이스 메서드를 재정의해야 한다 .

Page 33: Visual C++ ATL/COM Programming

33

다중 상속 시 메모리 구조

CHello::thisQueryInterfaceQueryInterfaceIHello vptrIHello vptr

AddRefAddRefReleaseReleasesayHellosayHello

QueryInterfaceQueryInterfaceAddRefAddRef

ReleaseReleasesayGoodbyesayGoodbye

IGoodbye vptrIGoodbye vptr

멤버 변수멤버 변수

IHello

IGoodbye

(IHello*)CHello::this

(IGoodbye*)CHello ::this

Page 34: Visual C++ ATL/COM Programming

34

QueryInterface 의 구현HRESULT _stdcall CHello::QueryInterface(REFIID riid, LPVOID* ppv){ HRESULT hr = E_NOINTERFACE; *ppv = NULL;

if(riid == IID_IUnknown || riid == IID_IHello) *ppv = static_cast<IHello*>( this ); else if(riid == IID_IGoodbye) *ppv = static_cast<IGoodbye*>( this )

if(*ppv != NULL) { AddRef(); return S_OK; } return hr; }

HRESULT _stdcall CHello::QueryInterface(REFIID riid, LPVOID* ppv){ HRESULT hr = E_NOINTERFACE; *ppv = NULL;

if(riid == IID_IUnknown || riid == IID_IHello) *ppv = static_cast<IHello*>( this ); else if(riid == IID_IGoodbye) *ppv = static_cast<IGoodbye*>( this )

if(*ppv != NULL) { AddRef(); return S_OK; } return hr; }

Page 35: Visual C++ ATL/COM Programming

35

AddRef, Release 의 구현

ULONG _stdcall CHello::AddRef(){ return InterlockedIncrement(&m_cRef); }

ULONG _stdcall CHello::Release(){ if(InterlockedDecrement(&m_cRef) == 0) { delete this; } return m_cRef;}

ULONG _stdcall CHello::AddRef(){ return InterlockedIncrement(&m_cRef); }

ULONG _stdcall CHello::Release(){ if(InterlockedDecrement(&m_cRef) == 0) { delete this; } return m_cRef;}

InterlockedIncrement, InterlockedDecrement 함수 thread-safe 하게 증가 , 감소

Page 36: Visual C++ ATL/COM Programming

36

COM 객체 서비스 메서드 구현 COM 객체 고유한 서비스를 제공하는

인터페이스의 메소드를 구현 필요 시 생성자 , 소멸자 , 멤버 변수 추가 가능 COM 객체의 CLSID 정의

GUIDGEN 이용

Page 37: Visual C++ ATL/COM Programming

37

클래스 팩토리의 구현 COM 컴포넌트는 COM 객체의 인스턴스를

생성할 수 있는 매커니즘 제공해야 함 클래스 팩토리가 COM 객체의 인스턴스를

생성 클래스 팩토리도 일종의 COM 객체 IClassFactory 를 반드시 제공해야 함

Page 38: Visual C++ ATL/COM Programming

38

IClassFactoryinterface IClassFactory : IUnknown { HRESULT __stdcall CreateInstance( LPUNKNOWN pUnkOuter, REFIID iid, LPVOID* ppv) = 0; HRESULT __stdcall LockServer(BOOL bLock) = 0;};

interface IClassFactory : IUnknown { HRESULT __stdcall CreateInstance( LPUNKNOWN pUnkOuter, REFIID iid, LPVOID* ppv) = 0; HRESULT __stdcall LockServer(BOOL bLock) = 0;};

Page 39: Visual C++ ATL/COM Programming

39

컴포넌트의 생성 과정Client Server

CoGetClassObject 호출

COM LibraryCoGetClassObjectCoGetClassObject DllGetClassObjectDllGetClassObject

CFactoryCFactory

클래스팩토리생성

컴포넌트생성

pIClassFactory

IClassFactory 리턴

IClassFactory::CreateInstance 호출

pIHellopIHello::sayHello 호출

IHello 리턴

pIClassFactory->Release() 호출CHelloCHello

Page 40: Visual C++ ATL/COM Programming

40

컴포넌트 생성 과정 ( 코드 )

HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv){ *ppv = NULL; IClassFactory* pIFactory = NULL; HRESULT hr = CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, (LPVOID*) &pIFactory); if( SUCCEEDED(hr) ) { hr = pIFactory->CreateInstance(pUnkOuter, riid, ppv); pIFactory->Release(); } return hr;}

HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv){ *ppv = NULL; IClassFactory* pIFactory = NULL; HRESULT hr = CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, (LPVOID*) &pIFactory); if( SUCCEEDED(hr) ) { hr = pIFactory->CreateInstance(pUnkOuter, riid, ppv); pIFactory->Release(); } return hr;}

Page 41: Visual C++ ATL/COM Programming

41

인 - 프로세스 서버의 구현 클라이언트에서 COM 객체 사용 시 호출되는

4 개의 익스포트 함수 구현 DllGetClassObject

CoGetClassObject 에 의해 호출 DllRegisterServer, DllUnregisterServer

REGSVR32.EXE 에 의해 호출 DllCanUnloadNow

CoFreeUnusedLibrary 에 의해 호출

Page 42: Visual C++ ATL/COM Programming

42

로컬 서버 구현 Win32 에서는 프로세스마다 주소공간이

다르므로 로컬 서버나 리모트 서버가 리턴한 인터페이스 포인터는 클라이언트 주소공간에서는 의미가 없다 .

위치 투명성을 제공하기 위해서는 프록시와 스텁이 필요

Page 43: Visual C++ ATL/COM Programming

43

프록시 (Proxy)/ 스텁 (Stub)

ClientEXE

ClientEXE

ProxyDLL

ProxyDLL

ServerEXE

ServerEXE

StubDLL

StubDLL

Process BoundaryNetwork Boundary

LPCRPC

마샬링 언마샬링

Page 44: Visual C++ ATL/COM Programming

44

마샬링1. 표준 인터페이스의 마샬링

운영체제에 의해 제공 (ole32.dll)

2. 자동화 인터페이스의 마샬링 운영체제에 의해 제공 (oleaut32.dll)

3. 커스텀 인터페이스의 마샬링 MIDL 컴파일 결과로 만들어지는 코드 이용 IDL 작성 시 속성 지정

Page 45: Visual C++ ATL/COM Programming

45

로컬 서버의 구현 인 - 프로세스 서버와는 달리 CoInitialize, CoU

ninitialize 호출 DllRegisterServer, DllUnregisterServer

명령행 인자 처리로 구현 /RegServer, /UnregServer 옵션 처리

DllGetClassObject CoRegisterClassObject, CoRevokeClassObject

이용 DllCanUnloadNow

스스로 능동적으로 종료

Page 46: Visual C++ ATL/COM Programming

46

CoRegisterClassObject (1) COM 은 내부적으로 등록된 클래스 팩토리 CO

M 객체를 저장하는 ROT 관리 클라이언트가 CoGetClassObject 호출 시 RO

T 부터 검사함 등록이 안된 경우 /Embedding 옵션으로 로컬

서버 실행 클래스 팩토리 COM 객체를 ROT 에 등록함

Page 47: Visual C++ ATL/COM Programming

47

CoRegisterClassObject (2) STDAPI CoRegisterClassObject(REFCLSID

rclsid, IUnknown* pUnk, DWORD dwClsContext, DWORD flags, LPDWORD lpdwRegister)

rclsid : COM 객체의 CLSID pUnk : 클래스 팩토리 객체의 IUnknown* flags : REGCLS_SINGLEUSE,

REGCLS_MULTI_SEPARATE, REGCLS_MULTIPLEUSE lpdwRegister : 클래스 팩토리에 대한 매직 쿠키

(CoRevokeClassObject 에서 사용 )

Page 48: Visual C++ ATL/COM Programming

48

로컬 서버 종료 다음 조건 만족 시 능동적으로 종료

COM 객체 카운터가 0 이고 클라이언트가 IClassFactory::LockServer(FALSE) 를 호출함으로써 마지막 로크 카운터가 0 이 될 때

로크 카운터가 현재 0 이고 , 클라이언트가 IUnknown::Release 를 호출하여 마지막 COM 객체 카운터가 0 이 될 때

PostQuitMessage 함수 이용

Page 49: Visual C++ ATL/COM Programming

49

VC++ COM 컴파일러

Page 50: Visual C++ ATL/COM Programming

50

VC++ 의 COM 지원 #import __declspec 확장 속성 : uuid, property __uuidof _com_ptr_t 클래스 _com_error 클래스 _bstr_t 클래스 _variant_t 클래스

Page 51: Visual C++ ATL/COM Programming

51

형식 라이브러리 COM 컴포넌트가 노출하는 COM 객체에 대한

정보를 포함하는 일종의 복합 도큐먼트 (*.tlb) COM 객체에 대한 매뉴얼 IDL 의 library 문과 coclass 문에 의해 제공 DLL 이나 EXE 파일에 리소스로 포함 가능 (

리소스 스크립트에 선언 ) 형식 라이브러리 자체도 ITypeLib, ITypeLib2, ITy

peInfo, ITypeInfo2, ITypeComp 인터페이스를 노출하는 하나의 COM 객체임

HKEY_CLASSES_ROOT\TypeLib 서브키에 저장

Page 52: Visual C++ ATL/COM Programming

52

#import #import “파일이름” [속성 리스트 ] #import “progid:XXX.XXX.XXX” [속성 리스트 ] #import “libid:XXXXXX” [속성 리스트 ]

속성 리스트 : no_namespace, named_guids 2 개의 C++ 소스 코드 생성

*.tlh : 스마트 포인터 클래스 , 인터페이스의 정의 포함

*.tli : COM 객체의 메서드를 호출하는 구현 코드 포함 ( 래퍼 함수 )

Page 53: Visual C++ ATL/COM Programming

53

__declspec(uuid())__uuidof() COM 객체나 인터페이스에 GUID 지정

__uuidof() : 클래스 명이나 인터페이스 명에 대한 GUID 값 리턴

__uuidof(Hello); __uuidof(IHello);

struct __declspec(uuid(“60bb225d-a06f-11d1-9138-0020af715af0”)) Hello;struct __declspec(uuid(“60bb225b-a06f-11d1-9138-0020af715af0”)) IHello;

struct __declspec(uuid(“60bb225d-a06f-11d1-9138-0020af715af0”)) Hello;struct __declspec(uuid(“60bb225b-a06f-11d1-9138-0020af715af0”)) IHello;

Page 54: Visual C++ ATL/COM Programming

54

스마트 포인터 인터페이스 포인터를 관리해주는 클래스 operator-> 함수를 제공

Page 55: Visual C++ ATL/COM Programming

55

_com_ptr_t 클래스 (1) 스마트 포인터 기능을 제공 CoCreatInstance, AddRef, Release, QueryInter

face 호출 자동화 COM 객체의 속성 / 메서드 접근 시 -> 사용

_COM_SMARTPTR_TYPEDEF(IHello, __uuidof(IHello));

IHellpPtr pIHello(CLSID_Hello); // COM 객체 생성IGoodbye pIGoodbye = pIHello; // IGoodbye 인터페이스 구함pIHello->sayHello(name, &message);pIHello->name = (BSTR) buffer;name = (wchar_t*) pIHello->name;

_COM_SMARTPTR_TYPEDEF(IHello, __uuidof(IHello));

IHellpPtr pIHello(CLSID_Hello); // COM 객체 생성IGoodbye pIGoodbye = pIHello; // IGoodbye 인터페이스 구함pIHello->sayHello(name, &message);pIHello->name = (BSTR) buffer;name = (wchar_t*) pIHello->name;

Page 56: Visual C++ ATL/COM Programming

56

_com_ptr_t 클래스 (2) 멤버함수

AddRef, Release, QueryInterface : 캡슐화된 인터페이스 포인터에 대한 IUnknown 함수 호출

CreateInstance : CLSID 나 ProgID 를 인자로 COM 객체의 인스턴스 생성

operator= : 인터페이스 포인터를 구함 _com_ptr_t 의 멤버 접근 시 . 사용

IHelloPtr pIHello;pIHello.CreateInstance(__uuidof(Hello)); // COM 객체 생성IGoodbye pIGoodbye;pIGoodbye.QueryInterface(__uuidof(IGoodbye), &pIGoodbye);pIHello = 0; 또는 pIHello.Release();

IHelloPtr pIHello;pIHello.CreateInstance(__uuidof(Hello)); // COM 객체 생성IGoodbye pIGoodbye;pIGoodbye.QueryInterface(__uuidof(IGoodbye), &pIGoodbye);pIHello = 0; 또는 pIHello.Release();

Page 57: Visual C++ ATL/COM Programming

57

_com_error 클래스 (1) *.tli 의 메서드 구현 코드

COM 객체의 메서드의 HRESULT 값을 검사하여 실패한 경우 _com_issue_error, _com_issue_errorex 함수 호출

_com_error 형의 예외를 throw

try { IHello pIHello(__uuidof(Hello)); …}catch(_com_error& e) { AfxMessageBox(e.ErrorMessage());}

try { IHello pIHello(__uuidof(Hello)); …}catch(_com_error& e) { AfxMessageBox(e.ErrorMessage());}

Page 58: Visual C++ ATL/COM Programming

58

_com_error 클래스 (2) 멤버함수

Error : HRESULT 값 리턴 ErrorInfo : IErrorInfo 인터페이스 리턴 Description, HelpContext, HelpFile, Source, GUID :

IErrorInfo 메서드 제공 ErrorMessage : HRESULT 에 대한 문자열 형태의

에러 메시지 리턴

Page 59: Visual C++ ATL/COM Programming

59

_bstr_t, _variant_t 클래스 _bstr_t

BSTR 를 캡슐화 SysAllocString과 SysFreeString 호출

_variant_t VARIANT 를 캡슐화 VariantInit과 VariantClear 호출

Page 60: Visual C++ ATL/COM Programming

60

ATL 프로그래밍 기초

Page 61: Visual C++ ATL/COM Programming

61

Active Template Library ATL 의 목적

작고 빠르고 확장성을 갖는 COM 객체를 손쉽게 구현 (IUnknown, IClassFactory 의 구현 제공 )

MFC vs ATL MFC

풍부한 사용자 인터페이스 기능 제공 MFC 디폴트 프레임워크 크기 , 속도 문제

ATL 프레임워크 불필요 . 실행 라이브러리 불필요 작고 빠른 COM 컴포넌트 생성에 적합

업무 로직을 구현하는 자동화 컴포넌트 구현에 적합

Page 62: Visual C++ ATL/COM Programming

62

C++ 의 템플릿 (1) 범용형에 의해 함수 / 클래스 템플릿을 정의 템플릿의 인자에 의해 함수나 클래스 코드가 생성 꼭 필요한 코드만 생성 작고 효율적인 코드

template <typename T> class CStack {private: T* p; int sz;public: void Push(T a) { *p++ = a; } …};CStack <int> si(10);

template <typename T> class CStack {private: T* p; int sz;public: void Push(T a) { *p++ = a; } …};CStack <int> si(10);

Page 63: Visual C++ ATL/COM Programming

63

C++ 의 템플릿 (2)template <typename T> class CArray {…};CArray<int> intArray;class CIntArray : public CArray<int> {…};CArray< CArray<int> > intTwoDArray;

template <typename T> class CArray {…};CArray<int> intArray;class CIntArray : public CArray<int> {…};CArray< CArray<int> > intTwoDArray;

template <typename Base>class CComObject : public Base {…}; CComObject<CMyObject> *pObj;

template <typename Base>class CComObject : public Base {…}; CComObject<CMyObject> *pObj;

Page 64: Visual C++ ATL/COM Programming

64

ATL 프로젝트 마법사 (1) 서버 유형에 따라 ATL 프로젝트에 필요한 골격

코드와 프로젝트 파일 생성 서버 유형

동적 연결 라이브러리 (DLL) 인 - 프로세스 서버 COM 컴포넌트 생성 DllMain, DllGetClassObject, DllCanUnloadNow, DllRegisterServe

r, DllUnregisterServer 의 구현 코드 제공 실행 파일 (EXE)

아웃 - 오브 - 프로세스 서버 COM 컴포넌트 생성 WinMain안에 레지스트리 등록 및 해제 , ROT 에 클래스 팩토리

등록 및 해제 , 메시지 루프 등 기본 코드 제공 서비스 (Service)

Windows NT 서비스 생성

Page 65: Visual C++ ATL/COM Programming

65

ATL 프로젝트 마법사 (2) 추가 옵션

프록시 / 스텁 코드 병합 허용 COM 컴포넌트와 같은 DLL안에 프록시 / 스텁 코드 추가

MFC 지원 COM+ 1.0 지원

특성 사용 (ATL 7.0) attribute 기반 프로그래밍 지원 기능 추가 좀더 쉽게 COM 프로그래밍을 할 수 있도록 attrib

ute 제공 필요한 코드 생성

Page 66: Visual C++ ATL/COM Programming

66

서버 모듈 클래스 (ATL 3.0) CComModule 클래스

COM 서버 모듈 구현 클라이언트에서 COM 서버내의 COM 객체로 접근하는 기능 제공

인 - 프로세스 서버와 아웃 - 오브 - 프로세스 서버를 모두 지원

객체 맵 (Object Map) 을 사용하여 COM 서버 모듈에 포함된 COM 객체 관리

// COM 서버 모듈 전역 객체 정의CComModule _Module; // COM 서버 모듈 전역 객체 정의CComModule _Module;

Page 67: Visual C++ ATL/COM Programming

67

서버 모듈 클래스 (ATL 3.0) CComModule 멤버

Init : 멤버 변수 초기화 Term : 멤버 변수 해제 GetClassObject : 클래스 팩토리 생성 (DLL 전용 ) GetLockCount : 로크 카운터 리턴 RegisterServer : 레지스트리에 객체 맵에 정의된 CO

M 객체 등록 UnregisterServer : 레지스트리에서 객체 맵에

정의된 COM 객체의 등록 해제 RegisterClassObjects : ROT 에 클래스 팩토리 등록

(EXE 전용 ) RevokeClassObjects : ROT 에서 클래스 팩토리의

등록 해제 (EXE 전용 )

Page 68: Visual C++ ATL/COM Programming

68

서버 모듈 클래스 (ATL 7.0) 인 - 프로세스 서버와 아웃 - 오브 - 프로세스

서버의 기능을 나눠서 제공할 수 있도록 계층 구조 정의

CAtlDllModuleTCAtlDllModuleT CAtlExeModuleTCAtlExeModuleT

CAtlModuleTCAtlModuleT

CAtlModuleCAtlModule

ATL_MODULEATL_MODULE

Page 69: Visual C++ ATL/COM Programming

69

객체 맵 (Object Map) ATL 단순 개체 마법사로 COM 객체를 생성할

때마다 객체 맵에 생성된 COM 객체에 대한 정보 추가

_ATL_OBJMAP_ENTRY 구조체 배열 객체 맵의 정보

레지스트리에 COM 객체 등록 및 해제 방법 클래스 팩토리 생성 방법 COM 객체 생성 방법

BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Hello, CHello)END_OBJECT_MAP()

BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Hello, CHello)END_OBJECT_MAP()

Page 70: Visual C++ ATL/COM Programming

70

객체 맵 (Object Map) (ATL 7.0) ATL 7.0 에서는 별도의 객체 맵을 따로

관리하는 대신 OBJECT_ENTRY_AUTO 매크로 사용 OBJECT_ENTRY_AUTO 매크로는 COM 서버 내에

COM 객체를 추가할 때마다 COM 객체의 구현 클래스 헤더 파일에 함께 생성됨

링크 단계에서 객체 맵을 링커가 하나로 합쳐줌

Page 71: Visual C++ ATL/COM Programming

71

ATL 단순 개체 마법사 COM 서버에 COM 객체 추가 COM 객체의 카테고리와 유형 선택 이름 페이지

약식 이름 : COM 객체명 입력 속성 페이지

스레딩 모델 인터페이스 집합체 지원

Page 72: Visual C++ ATL/COM Programming

72

ATL 기초 클래스 ATL 단순 개체 마법사가 생성하는 COM 객체

클래스의 구현

class ATL_NO_VTABLE CHello : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CHello, &CLSID_Hello>, public IHello{…};

class ATL_NO_VTABLE CHello : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CHello, &CLSID_Hello>, public IHello{…};

Page 73: Visual C++ ATL/COM Programming

73

ATL COM 객체의 계층 구조

CComObjectRootExCComObjectRootEx CComCoClassCComCoClass

CHelloCHello

CCom(Agg)ObjectCCom(Agg)Object

COM 객체의구현클래스

추상클래스

COM 객체의인스턴스 생성시사용되는 클래스

Page 74: Visual C++ ATL/COM Programming

74

CComObjectRootEx IUnknown 인터페이스의 내부 구현 제공 스레딩 모델에 따른 레퍼런스 카운트 구현

CComSingleThreadingModel CComMultiThreadingModel

멤버 함수 InternalQueryInterface, InternalAddRef, InternalRele

ase : IUnknown 의 내부 구현 OuterQueryInterface, OuterAddRef, OuterRelease :

통합시 외부 COM 객체로 IUnknown 을 위임하는 코드 구현

FinalConstruct, FinalRelease : COM 객체의 초기화 , 해제를 위한 가상 함수

Page 75: Visual C++ ATL/COM Programming

75

CComCoClass (1) DECLARE_CLASSFACTORY() 매크로

COM 객체의 디폴트 클래스 팩토리 정의 CComClassFactory

IClassFactory 의 구현 제공 CComCreator

클래스 팩토리 COM 객체의 인스턴스 생성

DECLARE_CLASSFACTORY() 확장후typedef CComCreator< CComObject< CComClassFactory > > _ClassFactoryCreatorClass;

DECLARE_CLASSFACTORY() 확장후typedef CComCreator< CComObject< CComClassFactory > > _ClassFactoryCreatorClass;

Page 76: Visual C++ ATL/COM Programming

76

CComCoClass (2) DECLARE_AGGREGATABLE() 매크로

COM 객체가 통합되어 사용될 수 있음 통합되지 않을 때는 CComObject 클래스를 사용 통합될 때는 CComAggObject 클래스 사용

DECLARE_AGGREGATABLE(CHello) 확장후typedef CComCreator2< CComCreator< CComObject< CHello > >, CComCreator< CComAggObject< CHello > > > _CreatorClass;

DECLARE_AGGREGATABLE(CHello) 확장후typedef CComCreator2< CComCreator< CComObject< CHello > >, CComCreator< CComAggObject< CHello > > > _CreatorClass;

Page 77: Visual C++ ATL/COM Programming

77

CCom(Agg)Object 사용자 COM 객체 클래스를 템플릿의 인자로 받아 사용자 COM 객체 클래스를 상속받는 클래스로 정의됨

QueryInterface, AddRef, Release 의 구현 제공 실제 COM 객체의 인스턴스 생성 시 이용되는

클래스임

template <class Base>class CComObject : public Base{…};

template <class Base>class CComObject : public Base{…};

Page 78: Visual C++ ATL/COM Programming

78

ATL_NO_VTABLE __declspec(novtable) 로 확장 클래스의 생성자와 소멸자에서 가상함수

테이블과 vptr 가 초기화되는 것을 막아줌 사용자 COM 객체 클래스는 추상클래스

이므로 가상함수 테이블이나 vptr 을 초기화할 필요가 없음 파생클래스 (CComObject) 에서 처리됨

Page 79: Visual C++ ATL/COM Programming

79

COM Map COM 객체 클래스 선언부에 COM 맵 정의 COM 맵을 사용하여 COM 객체가 제공하는

각 인터페이스를 관리 CComObjectRootEx::InternalQueryInterface 의

구현에서 COM 맵 정보를 사용

BEGIN_COM_MAP(CHello) COM_INTERFACE_ENTRY(IHello)END_COM_MAP()

BEGIN_COM_MAP(CHello) COM_INTERFACE_ENTRY(IHello)END_COM_MAP()

Page 80: Visual C++ ATL/COM Programming

80

Property, Method 추가 속성 (Property) 추가 마법사

클래스뷰에서 ‘속성 추가’ 선택 속성 형식 , 속성 이름 지정 함수 형식

Get 함수 : 읽기 함수 Put 함수 : 쓰기 함수

COM 객체 구현 클래스에 멤버 변수 추가 ( 초기화 )

메서드 (Method) 추가 마법사 클래스뷰에서 ‘메서드 추가’ 선택

Page 81: Visual C++ ATL/COM Programming

81

새로운 인터페이스의 추가 직접 수정하는 경우

IDL 파일에 인터페이스 정의 COM 객체 클래스가 인터페이스를 상속받도록

수정 COM 맵에 인터페이스 추가 인터페이스의 메서드 구현

인터페이스 구현 마법사를 사용하는 경우 IDL 파일에 인터페이스 정의 MIDL 컴파일 클래스뷰에서 ‘인터페이스 구현’ 선택

Page 82: Visual C++ ATL/COM Programming

82

CComPtr, CComQIPtr AddRef, Release 호출 자동화 CComQIPtr 은 QueryInterface 호출 자동화 _com_ptr_t 와는 달리 인스턴스 생성 기능 없음

CComPtr 은 다른 인터페이스 수용 불가

CComPtr<IUnknown> pUnk;CComPtr<IHello> pHello1; CComPtr<IHello> pHello2(pUnk); // 에러

CComPtr<IUnknown> pUnk;CComPtr<IHello> pHello1; CComPtr<IHello> pHello2(pUnk); // 에러

Page 83: Visual C++ ATL/COM Programming

83

CComBSTR, CComVariant CComBSTR

주요멤버 m_str : BSTR 값 Append, AppendBSTR, Copy, Empty, Length operator BSTR, =, +=, &, !

CComVariant 주요 멤버

ChangeType, Clear, Copy operator =, ==, !=

Page 84: Visual C++ ATL/COM Programming

84

ATL Debugging _ATL_DEBUG_INTERFACES

<atlbase.h>앞에 정의 Reference Counting과 QueryInterface 디버깅

_ATL_DEBUG_QI <atlcom.h>앞에 정의 QueryInterface 디버깅

ATLTRACE(…) Output 윈도우에 디버깅 정보 출력

_ATL_MIN_CRT CRT 라이브러리 제거 (Release 모드 )

Page 85: Visual C++ ATL/COM Programming

85

자동화와 이중 인터페이스

Page 86: Visual C++ ATL/COM Programming

86

자동화 (Automation) 어플리케이션이 자신의 기능을 다른

어플리케이션에 노출하는 기능을 자동화라고 함 자동화 컴포넌트 , 자동화 서버

자동화 서비스를 제공하는 어플리케이션 자동화 컨트롤러 , 자동화 클라이언트

자동화 서비스를 사용하는 어플리케이션 자동화 객체

자동화 서비스를 제공하는 객체 메서드 (Method), 속성 (Property), 이벤트

(Event) 를 통해 기능 제공

Page 87: Visual C++ ATL/COM Programming

87

자동화의 특징 스크립트 언어에서는 COM 객체를 사용할 수

없음 가상함수 테이블을 인식할 수 없음 자료형 개념이 불분명함

이를 해결하기 위한 방법이 자동화 기술 가상함수 테이블 인식 문제 IDispatch 로 해결 자료형 문제 자동화 데이터 타입으로 해결

Page 88: Visual C++ ATL/COM Programming

88

자동화의 장단점 장점

스크립트 클라이언트에서도 자동화 객체 사용 가능 마샬링 코드를 운영체제가 제공 (oleaut32.dll)

단점 실행 속도가 저하됨 사용할 수 있는 데이터형이 제한됨

Page 89: Visual C++ ATL/COM Programming

89

IDispatch

interface IDispatch : IUnknown {

HRESULT GetTypeInfoCount(UINT* pctinfo);HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);HRESULT GetIDsOfNames(const IID& iid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);HRESULT Invoke(DISPID dispIdMember, const IID& iid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* piArrErr);

};

interface IDispatch : IUnknown {

HRESULT GetTypeInfoCount(UINT* pctinfo);HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);HRESULT GetIDsOfNames(const IID& iid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);HRESULT Invoke(DISPID dispIdMember, const IID& iid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* piArrErr);

};

Page 90: Visual C++ ATL/COM Programming

90

자동화의 구현 디스패치 인터페이스 (dispinterface)

Invoke 함수는 DISPID 를 함수 포인터 배열의 인덱스로 사용하여 함수 호출

COM 인터페이스가 아님 불변성 X 메서드가 HRESULT 를 리턴할 필요 없음

이중 인터페이스 (dual interface) dispinterface 의 속도 저하 문제 개선 IDispatch 를 상속받는 COM interface 를 사용

속도 개선 효과

Page 91: Visual C++ ATL/COM Programming

91

후기 바인딩 vs 초기 바인딩 후기 바인딩

실행 시간에 DISPID 결정 속도가 느리며 잘못된 속성명이나 메서드명 사용

시 실행 에러 발생 컴파일 시 데이터 형 검사 X 실행 에러 발생 실행 시간에 자동화 객체의 기능 결정

초기 바인딩 ID 바인딩 형식 라이브러리에서 DISPID 정보를 읽어서 사용

후기 바인딩에 비해 속도가 빠름 컴파일 시 데이터 형 검사 가능

Page 92: Visual C++ ATL/COM Programming

92

VARIANT 데이터형 서로 다른 데이터형을 멤버로 갖는 16 byte

크기의 공용체 (union) VARIANT 관련 함수

VariantInit : VARIANT 구조체 초기화 VariantChangeType : 데이터형 변환

Page 93: Visual C++ ATL/COM Programming

93

BSTR, SAFEARRAY BSTR

byte-length prefixed unicode string SysAllocString, SysFreeString 이용

SAFEARRAY 배열 범위에 대한 정보를 포함하고 있는 배열 SafeArray… 함수 이용

Page 94: Visual C++ ATL/COM Programming

94

ATL 자동화 객체 ATL 단순개체마법사의 옵션 페이지에서

인터페이스 중 ‘이중’ 선택 IDispatchImpl<>

IDispatch 인터페이스의 디폴트 구현 코드 제공

class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public IDispatchImpl<IAddBack, &IID_IAddBack, &LIBID_ADDBACKLib>{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddBack) COM_INTERFACE_ENTRY(IDispatch)END_COM_MAP()};

class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public IDispatchImpl<IAddBack, &IID_IAddBack, &LIBID_ADDBACKLib>{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddBack) COM_INTERFACE_ENTRY(IDispatch)END_COM_MAP()};

Page 95: Visual C++ ATL/COM Programming

95

MFC 자동화 클라이언트 ‘클래스 추가’에서 ‘ TypeLib 의 MFC 클래스’

로 자동화 객체에 대한 wrapper class 생성 CAddBack COleDispatchDriver 의 파생 클래스 IDispatch 를 통한 메서드 , 속성 호출을 처리하는 멤버 함수 제공

자동화 객체 생성시 COleDispatchDriver::CreateDispatch 이용 ProgID 나 CLSID 로 자동화 객체 생성

Page 96: Visual C++ ATL/COM Programming

96

Multiple Dual Interface (1) 여러 개의 dual interface 를 갖는 자동화 객체

구현 가능

[…, dual, …]interface IAddEnd : IDispatch{…};[…, dual, …]interface IAdd : IDispatch{…};

[…, dual, …]interface IAddEnd : IDispatch{…};[…, dual, …]interface IAdd : IDispatch{…};

[…]library ADDBACKLib{ […] coclass AddBack { [default] interface IAddEnd; interface IAdd; };};

[…]library ADDBACKLib{ […] coclass AddBack { [default] interface IAddEnd; interface IAdd; };};

Page 97: Visual C++ ATL/COM Programming

97

Multiple Dual Interface (2) 스크립트 클라이언트는 디폴트 인터페이스로만

접근 가능class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public IDispatchImpl<IAddEnd, &IID_IAddEnd, &LIBID_ADDBACKLib>, public IDispatchImpl<IAdd, &IID_IAdd, &LIBID_ADDBACKLib>

{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddEnd) COM_INTERFACE_ENTRY2(IDispatch, IAddEnd) // 디폴트 인터페이스 COM_INTERFACE_ENTRY(IAdd)END_COM_MAP()};

class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadingModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public IDispatchImpl<IAddEnd, &IID_IAddEnd, &LIBID_ADDBACKLib>, public IDispatchImpl<IAdd, &IID_IAdd, &LIBID_ADDBACKLib>

{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddEnd) COM_INTERFACE_ENTRY2(IDispatch, IAddEnd) // 디폴트 인터페이스 COM_INTERFACE_ENTRY(IAdd)END_COM_MAP()};

Page 98: Visual C++ ATL/COM Programming

98

DISPID 인코딩 multiple dual interface 를 갖는 자동화 객체의

모든 기능을 제공하기 위한 방법 COM 객체 구현 클래스에 GetIDsOfNames, In

voke 를 재정의 GetIDsOfNames : DISPID 를 찾아 인터페이스

정보를 함께 인코딩하여 리턴 Invoke : 넘겨받은 DISPID 를 디코딩하여 어느

인터페이스의 함수를 호출할 지 결정

DISPID

인코딩된 DISPID (4 byte)인터페이스 구분

2 byte 2 byte

Page 99: Visual C++ ATL/COM Programming

99

에러 정보

자동화객체

ISupportErrorInfo

에러 정보객체

ICreateErrorInfo

IErrorInfo

CreateErrorInfo( 에러정보객체 생성 )

ICreateErrorInfo 로에러 정보 지정

에러정보객체의 IErrorInfo 구함

SetErrorInfo( 현재 스레드와 에러정보객체연결 )

자동화클라이언트

ISupportErrorInfo구함

GetErrorInfo 로 IErrorInfo 구함

InterfaceSupportsErrorInfo 호출하여 에러 정보 제공 여부 확인

IErrorInfo 로 에러 정보 추출

Page 100: Visual C++ ATL/COM Programming

100

에러 정보 리턴 (1) 자동화 객체에서는 IErrorInfo 객체를

생성하여 여기에 발생한 에러에 대한 자세한 정보를 제공

자동화 객체는 ISupportErrorInfo 인터페이스를 지원해야 함 ATL 단순개체마법사의 옵션 페이지의 ‘ ISupportErr

orInfo’ 지원 선택 InterfaceSupportsErrorInfo : 해당 인터페이스가

자세한 에러 정보를 제공하는 지 여부를 결정

Page 101: Visual C++ ATL/COM Programming

101

에러 정보 리턴 (2) 에러 정보 제공

CreateErrorInfo 함수를 호출하여 에러 정보 객체 생성

ICreateErrorInfo 인터페이스로 에러 정보 저장 SetSource : 에러가 발생한 자동화 객체명 지정 (ProgID) SetDescription : 에러에 대한 설명 텍스트 지정 SetGUID : 에러를 정의한 인터페이스의 GUID 지정 SetHelpFile : 에러를 설명하는 도움말파일의 경로명 지정 SetHelpContext : 에러에 대한 도움말 컨텍스트 ID 지정

SetErrorInfo 함수를 호출하여 에러 정보 객체를 현재의 스레드와 연결

Page 102: Visual C++ ATL/COM Programming

102

에러 정보 리턴 (3)STDMETHODIMP CAddBack::put_AddEnd(short newVal){ if (newVal < 1) { CComPtr<ICreateErrorInfo> pCreateErrInfo; CComQIPtr<IErrorInfo, &IID_IErrorInfo> pErrInfo;

HRESULT hr = ::CreateErrorInfo(&pCreateErrInfo); if(SUCCEEDED(hr)) { pCreateErrInfo->SetSource(OLESTR("AddBack.AddBack.3")); pCreateErrInfo->SetDescription(OLESTR("0 보다 큰 값을 입력하십시오 !")); pErrInfo = pCreateErrInfo; ::SetErrorInfo(0, pErrInfo); } return E_INVALIDARG; } m_AddEnd = newVal; return S_OK;}

STDMETHODIMP CAddBack::put_AddEnd(short newVal){ if (newVal < 1) { CComPtr<ICreateErrorInfo> pCreateErrInfo; CComQIPtr<IErrorInfo, &IID_IErrorInfo> pErrInfo;

HRESULT hr = ::CreateErrorInfo(&pCreateErrInfo); if(SUCCEEDED(hr)) { pCreateErrInfo->SetSource(OLESTR("AddBack.AddBack.3")); pCreateErrInfo->SetDescription(OLESTR("0 보다 큰 값을 입력하십시오 !")); pErrInfo = pCreateErrInfo; ::SetErrorInfo(0, pErrInfo); } return E_INVALIDARG; } m_AddEnd = newVal; return S_OK;}

Page 103: Visual C++ ATL/COM Programming

103

에러 정보 리턴 (4) CComCoClass::Error 함수 또는 AtlReportError

함수 이용

STDMETHODIMP CAddBack::put_AddEnd(short newVal){ if (newVal < 1) { return Error("0 보다 큰 값을 입력하십시오 !“, IID_IAddBack, E_INVALIDARG); } m_AddEnd = newVal; return S_OK;}

STDMETHODIMP CAddBack::put_AddEnd(short newVal){ if (newVal < 1) { return Error("0 보다 큰 값을 입력하십시오 !“, IID_IAddBack, E_INVALIDARG); } m_AddEnd = newVal; return S_OK;}

Page 104: Visual C++ ATL/COM Programming

104

에러 정보 받기 자동화 객체에서 에러 정보를 받는 방법

IDispatch::Invoke 의 EXCEPINFO 인자 사용 직접 에러 정보 객체를 사용

에러 발생시 ISupportErrorInfo 인터페이스를 구하여 InterfaceSupportsErrorInfo 함수를 호출 IErrorInfo 를 지원하는지 여부 확인

GetErrorInfo 를 호출하여 IErrorInfo 인터페이스를 구한 후 IErrorInfo 인터페이스의 메서드를 호출하여 에러 정보를 추출

GetSource, GetDescription, GetGUID, GetHelpFile, GetHelpContext 등 이용

Page 105: Visual C++ ATL/COM Programming

105

자동화 객체의 안전성 보장 (1) 레지스트리 키에 자동화 객체가 안전하다는 것을 표시하는 항목 추가 컴포넌트 카테고리 등록

CATID_SafeForScripting, CATID_SafeForInitializing 추가

#include <objsafe.h>class ATL_NO_VTABLE CAddBack : …{BEGIN_CATEGORY_MAP(CAddBack) IMPLEMENTED_CATEGORY(CATID_SafeForScripting) IMPLEMENTED_CATEGORY(CATID_SafeForInitiailizing)END_CATEGORY_MAP()};

#include <objsafe.h>class ATL_NO_VTABLE CAddBack : …{BEGIN_CATEGORY_MAP(CAddBack) IMPLEMENTED_CATEGORY(CATID_SafeForScripting) IMPLEMENTED_CATEGORY(CATID_SafeForInitiailizing)END_CATEGORY_MAP()};

Page 106: Visual C++ ATL/COM Programming

106

자동화 객체의 안전성 보장 (2) 자동화 객체에 IObjectSafety 인터페이스를

구현 IObjectSafetyImpl<> 을 상속 <atlctl.h> 포함 COM 맵에 IObjectSafety 추가

#include <atlctl.h>class ATL_NO_VTABLE CAddBack : … public IObjectSafetyImpl<CAddBack, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA >{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IObjectSafety)END_COM_MAP()};

#include <atlctl.h>class ATL_NO_VTABLE CAddBack : … public IObjectSafetyImpl<CAddBack, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA >{BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IObjectSafety)END_COM_MAP()};

Page 107: Visual C++ ATL/COM Programming

107

이벤트 구현

Page 108: Visual C++ ATL/COM Programming

108

이벤트 자동화 객체가 자신에게 어떠한 사건이 발생했다는

사실을 자동화 컨트롤러에게 알려주는 기능 커넥션 포인트 메커니즘으로 구현

자동화컨트롤러자동화

컨트롤러자동화

객체자동화

객체

Property/Method

Event

Page 109: Visual C++ ATL/COM Programming

109

커넥션 포인트 (1) 소스 객체 (source object)

이벤트를 발생 (fire) 시키는 소스 인터페이스 (source interface, outgoing interface) 를 제공

자동화 객체가 소스 객체가 됨 싱크 오브젝트

이벤트를 받아들이는 싱크 인터페이스 (sink interface, incoming interface) 를 제공

자동화 컨트롤러 내에 별도로 생성 이벤트

자동화 객체가 호출하고 싱크 객체가 구현하는 싱크 인터페이스의 메서드

싱크 인터페이스와 소스 인터페이스는 서로 일치

Page 110: Visual C++ ATL/COM Programming

110

커넥션 포인트 (2) 자동화 컨트롤러는 싱크 객체 생성 자동화 컨트롤러는 자동화 객체에게 IConnectionPoin

tContainer 요청 IConnectionPointContainer::FindConnectionPoint 호출

소스 인터페이스에 대한 IConnectionPoint 구함 IConnectionPoint::Advise 를 호출하여 싱크 객체의

인터페이스를 넘겨줌 자동화 객체와 자동화 컨트롤러 연결 자동화

객체는 필요할 때 이벤트를 발생 (fire) 시킴 IConnectionPoint::Unadvise 를 호출하여 커넥션

포인트를 단절

Page 111: Visual C++ ATL/COM Programming

111

소스 인터페이스의 정의 (1) 이중 인터페이스로 정의 dispinterface 로 정의 스크립트

클라이언트에서 사용 COM 객체에 추가 시 source 속성 지정

[ object, uuid(D90D5240-ACD0-11d1-913A-0020AF715AF0), dual, helpstring("IAddBackEvent Interface"), pointer_default(unique)]interface IAddBackEvent : IDispatch{};

[ object, uuid(D90D5240-ACD0-11d1-913A-0020AF715AF0), dual, helpstring("IAddBackEvent Interface"), pointer_default(unique)]interface IAddBackEvent : IDispatch{};

Page 112: Visual C++ ATL/COM Programming

112

소스 인터페이스의 정의 (2)[ uuid(B086DCA0-ACD9-11d1-913A-0020AF715AF0), nonextensible]dispinterface _IAddBackEvent{properties:methods:};…coclass AddBack{ [default] interface IAddBack; [default, source] dispinterface _IAddBackEvent; [source] interface IAddBackEvent;};

[ uuid(B086DCA0-ACD9-11d1-913A-0020AF715AF0), nonextensible]dispinterface _IAddBackEvent{properties:methods:};…coclass AddBack{ [default] interface IAddBack; [default, source] dispinterface _IAddBackEvent; [source] interface IAddBackEvent;};

Page 113: Visual C++ ATL/COM Programming

113

커넥션 포인트 클래스 생성 MIDL 컴파일 클래스뷰의 ‘연결 지점 추가’ 사용 커넥션 포인트 클래스 생성

template <class T>class CProxyIAddBackEvent : public IConnectionPointImpl<T, &IID_IAddBackEvent, CComDynamicUnkArray>{};

template <class T>class CProxy_IAddBackEvent : public IConnectionPointImpl<T, &DIID__IAddBackEvent, CComDynamicUnkArray>{};

template <class T>class CProxyIAddBackEvent : public IConnectionPointImpl<T, &IID_IAddBackEvent, CComDynamicUnkArray>{};

template <class T>class CProxy_IAddBackEvent : public IConnectionPointImpl<T, &DIID__IAddBackEvent, CComDynamicUnkArray>{};

Page 114: Visual C++ ATL/COM Programming

114

자동화 객체 클래스 수정 ATL 단순 개체 마법사의 옵션 페이지에서 ‘연결 지점 지원’ 옵션 선택 IConnectionPointContainer 의 구현 생성

class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public ISupportErrorInfo, public IConnectionPointContainerImpl<CAddBack>, public CProxyIAddBackEvent<CAddBack>, public CProxy_IAddBackEvent<CAddBack>, public IDispatchImpl<IAddBack, &IID_IAddBack, &LIBID_ADDBACKLib>{…};

class ATL_NO_VTABLE CAddBack : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CAddBack, &CLSID_AddBack>, public ISupportErrorInfo, public IConnectionPointContainerImpl<CAddBack>, public CProxyIAddBackEvent<CAddBack>, public CProxy_IAddBackEvent<CAddBack>, public IDispatchImpl<IAddBack, &IID_IAddBack, &LIBID_ADDBACKLib>{…};

Page 115: Visual C++ ATL/COM Programming

115

커넥션 포인트 맵 IConnectionPointContainer::FindConnectionPoint 가

소스 인터페이스에 대한 IConnectionPoint 인터페이스를 찾는 방법 제공

BEGIN_CONNECTION_POINT_MAP(CAddBack) CONNECTION_POINT_ENTRY(IID_IAddBackEvent) CONNECTION_POINT_ENTRY(DIID__IAddBackEvent)END_CONNECTION_POINT_MAP()

BEGIN_CONNECTION_POINT_MAP(CAddBack) CONNECTION_POINT_ENTRY(IID_IAddBackEvent) CONNECTION_POINT_ENTRY(DIID__IAddBackEvent)END_CONNECTION_POINT_MAP()

Page 116: Visual C++ ATL/COM Programming

116

이벤트 발생 이벤트를 발생하려면 커넥션 포인트 클래스에

정의된 Fire_ 메서드 호출STDMETHODIMP CAddBack::put_AddEnd(short newVal){ … if(m_AddEnd != newVal) { m_AddEnd = newVal; CProxyIAddBackEvent<CAddBack>::Fire_ChangedAddEnd( m_AddEnd); CProxy_IAddBackEvent<CAddBack>::Fire_ChangedAddEnd( m_AddEnd); } return S_OK;}

STDMETHODIMP CAddBack::put_AddEnd(short newVal){ … if(m_AddEnd != newVal) { m_AddEnd = newVal; CProxyIAddBackEvent<CAddBack>::Fire_ChangedAddEnd( m_AddEnd); CProxy_IAddBackEvent<CAddBack>::Fire_ChangedAddEnd( m_AddEnd); } return S_OK;}

Page 117: Visual C++ ATL/COM Programming

117

자동화 컨트롤러의 이벤트 받기 싱크 객체의 구현 필요 ATL 이용 MFC 어플리케이션에서의 ATL 사용

ATL 헤더 포함 CComModule 의 전역 오브젝트 정의

초기화 및 해제 필요

// stdafx.h#include <atlbase.h>extern CComModule _Module;#include <atlcom.h>

// 전역 오브젝트 정의CAddFrontApp theApp;CComModule _Module;

// stdafx.h#include <atlbase.h>extern CComModule _Module;#include <atlcom.h>

// 전역 오브젝트 정의CAddFrontApp theApp;CComModule _Module;

BOOL CAddFrontApp::InitInstance(){ _Module.Init(NULL, m_hInstance);}BOOL CAddFrontApp::ExitInstance(){ _Module.Term();}

BOOL CAddFrontApp::InitInstance(){ _Module.Init(NULL, m_hInstance);}BOOL CAddFrontApp::ExitInstance(){ _Module.Term();}

Page 118: Visual C++ ATL/COM Programming

118

싱크 객체 구현

class ATL_NO_VTABLE CEventHandler : public CComObjectRoot, public IDispatchImpl<IAddBackEvent, &IID_IAddBackEvent, &LIBID_ADDBACKLib>{public: BEGIN_COM_MAP(CEventHandler) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IAddBackEvent) END_COM_MAP()

STDMETHOD(raw_ChangedAddEnd) (short newVal); STDMETHOD(raw_ChangedSum) (short newVal);};

class ATL_NO_VTABLE CEventHandler : public CComObjectRoot, public IDispatchImpl<IAddBackEvent, &IID_IAddBackEvent, &LIBID_ADDBACKLib>{public: BEGIN_COM_MAP(CEventHandler) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IAddBackEvent) END_COM_MAP()

STDMETHOD(raw_ChangedAddEnd) (short newVal); STDMETHOD(raw_ChangedSum) (short newVal);};

tlh 파일에 정의된 소스 인터페이스 메서드 구현

Page 119: Visual C++ ATL/COM Programming

119

싱크 객체 생성 싱크 객체 생성 후 AtlAdvise 호출 소스 객체와 연결

BOOL CAddFrontDlg::OnInitDialog() { CComObject<CEventHandler>* pHandler; CComObject<CEventHandler>::CreateInstance(&pHandler); // 싱크 생성 IUnknownPtr pUnk = m_pAddBack; HRESULT hr = AtlAdvise(pUnk, pHandler->GetUnknown(), // 연결 IID_IAddBackEvent, &m_Cookie); if(FAILED(hr)) AfxMessageBox("이벤트를 받을 수 없습니다 ."); …

BOOL CAddFrontDlg::OnInitDialog() { CComObject<CEventHandler>* pHandler; CComObject<CEventHandler>::CreateInstance(&pHandler); // 싱크 생성 IUnknownPtr pUnk = m_pAddBack; HRESULT hr = AtlAdvise(pUnk, pHandler->GetUnknown(), // 연결 IID_IAddBackEvent, &m_Cookie); if(FAILED(hr)) AfxMessageBox("이벤트를 받을 수 없습니다 ."); …

Page 120: Visual C++ ATL/COM Programming

120

싱크 객체 소멸 자동화 객체 사용후 AtlUnadvise 호출 연결

해제

void CAddFrontDlg::~CAddFrontDlg() { IUnknownPtr pUnk = m_pAddBack; AtlUnadvise(pUnk, IID_IAddBackEvent, m_Cookie); // 연결 해제

m_pAddBack = 0;}

void CAddFrontDlg::~CAddFrontDlg() { IUnknownPtr pUnk = m_pAddBack; AtlUnadvise(pUnk, IID_IAddBackEvent, m_Cookie); // 연결 해제

m_pAddBack = 0;}

Page 121: Visual C++ ATL/COM Programming

121

dispinterface 싱크 객체 구현 (1) 싱크 인터페이스

이중 인터페이스 또는 dispinterface 로 구현 이중 인터페이스를 구현하는 싱크 객체의 경우

모든 이벤트를 구현해야 함 dispinterface 를 구현하는 싱크 객체의 경우 모든

이벤트를 구현할 필요가 없음 이벤트 싱크 구현을 위한 IDispEventImpl<>

클래스 사용

Page 122: Visual C++ ATL/COM Programming

122

dispinterface 싱크 객체 구현 (2)

class CEventHandler : public IDispEventImpl<0,CEventHandler, // 파생 클래스 이름&DIID__IAddBackEvent, // .tlh 참조&LIBID_ADDBACKLib, // .tlh 참조1, // type library 의 major version #0> // type library 의 minor version #

{public: void __stdcall OnChangedAddEnd(short newVal); void __stdcall OnChangedSum(short newVal);

BEGIN_SINK_MAP(CEventHandler) SINK_ENTRY_EX(0, DIID__IAddBackEvent, 1, OnChangedAddEnd) SINK_ENTRY_EX(0, DIID__IAddBackEvent, 2, OnChangedSum) END_SINK_MAP()};

class CEventHandler : public IDispEventImpl<0,CEventHandler, // 파생 클래스 이름&DIID__IAddBackEvent, // .tlh 참조&LIBID_ADDBACKLib, // .tlh 참조1, // type library 의 major version #0> // type library 의 minor version #

{public: void __stdcall OnChangedAddEnd(short newVal); void __stdcall OnChangedSum(short newVal);

BEGIN_SINK_MAP(CEventHandler) SINK_ENTRY_EX(0, DIID__IAddBackEvent, 1, OnChangedAddEnd) SINK_ENTRY_EX(0, DIID__IAddBackEvent, 2, OnChangedSum) END_SINK_MAP()};

Page 123: Visual C++ ATL/COM Programming

123

dispinterface싱크 객체 생성 IDispEventImpl::DispEventAdvise 호출 소스 연결

BOOL CAddFrontDlg::OnInitDialog() { … IUnknownPtr pUnk = m_pAddBack;

m_pEventHandler = new CEventHandler; HRESULT hr = m_pEventHandler->DispEventAdvise(pUnk);

if(FAILED(hr)) AfxMessageBox("이벤트를 받을 수 없습니다 ."); …

BOOL CAddFrontDlg::OnInitDialog() { … IUnknownPtr pUnk = m_pAddBack;

m_pEventHandler = new CEventHandler; HRESULT hr = m_pEventHandler->DispEventAdvise(pUnk);

if(FAILED(hr)) AfxMessageBox("이벤트를 받을 수 없습니다 ."); …

Page 124: Visual C++ ATL/COM Programming

124

dispinterface싱크 객체 소멸 IDispEventImpl::DispEventUnadvise 호출 연결 해제

void CAddFrontDlg::~CAddFrontDlg() { IUnknownPtr pUnk = m_pAddBack;

m_pEventHandler->DispEventUnadvise(pUnk);

delete m_pEventHandler;

m_pAddBack = 0;}

void CAddFrontDlg::~CAddFrontDlg() { IUnknownPtr pUnk = m_pAddBack;

m_pEventHandler->DispEventUnadvise(pUnk);

delete m_pEventHandler;

m_pAddBack = 0;}

Page 125: Visual C++ ATL/COM Programming

125

스크립트 클라이언트의싱크 객체 구현 스크립트 클라이언트는 실행 시간에 동적으로 싱크 객체 구현

이 때 싱크 인터페이스 정보를 얻기 위해 자동화 객체의 IProvideClassInfo2 이용

class ATL_NO_VTABLE CAddBack : public IProvideClassInfo2Impl<&CLSID_AddBack, &DIID__DIAddBackEvent, &LIBID_ADDBACKLib>, …{ BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) END_COM_MAP()};

class ATL_NO_VTABLE CAddBack : public IProvideClassInfo2Impl<&CLSID_AddBack, &DIID__DIAddBackEvent, &LIBID_ADDBACKLib>, …{ BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) END_COM_MAP()};

Page 126: Visual C++ ATL/COM Programming

126

COM 객체의 재사용

Page 127: Visual C++ ATL/COM Programming

127

COM 의 재사용성 COM 은 구현 상속은 제공하지 않음 인터페이스

상속만을 제공 이진 레벨의 재사용 기법

포함 (Containment) 통합 (Aggregation)

OuterOuter

InnerInner

Page 128: Visual C++ ATL/COM Programming

128

포함 (1) C++ 의 포함과 유사한 개념 외부 COM 객체는 내부 COM 객체를 이용하여

자신의 인터페이스를 구현 외부 COM 객체는 내부 COM 객체의 인터페이스

함수를 단순히 호출 Forwarding 가능 외부 COM 객체는 내부 COM 객체의 인터페이스

함수 호출에 다른 기능을 추가 Adding 가능 외부 COM 객체가 내부 COM 객체의 생성과

소멸을 모두 직접 관리 모든 COM 객체가 포함 가능

Page 129: Visual C++ ATL/COM Programming

129

포함 (2)

CAddBackCAddBackIAddBack

CAddEndCAddEndIAddEnd

m_pAddEnd CoCreateInstance( CLSID_AddEnd,…);

CLSID_AddEnd

CLSID_AddBack

IAddEnd

Page 130: Visual C++ ATL/COM Programming

130

통합 (1) 포함의 특별한 경우로 볼 수 있음 외부 COM 객체가 내부 COM 객체의

인터페이스를 직접 클라이언트에게 제공 외부 COM 객체는 인터페이스를 구현하지 않음 Forwarding O, Adding X

외부 COM 객체는 내부 COM 객체 생성 시 자신의 IUnknown 을 제공

외부 COM 객체는 내부 COM 객체 접근 시 내부 COM 객체의 IUnknown으로만 접근

통합을 지원하는 COM 객체만 통합 가능

Page 131: Visual C++ ATL/COM Programming

131

통합 (2)

CAddBackCAddBackIAddBack

CAddEndCAddEnd

m_pUnk

CoCreateInstance( CLSID_AddEnd, (IUnknown*)this, CLSCTX_ALL, IID_IUnknown, (void**)&m_pUnk);

CLSID_AddEnd

CLSID_AddBack

IAddEnd

Page 132: Visual C++ ATL/COM Programming

132

ATL 의 포함 구현 내부 COM 객체의 인터페이스를 저장할 멤버 정의 FinalConstruct 를 재정의하여 내부 COM 객체의

인스턴스 생성 메서드 구현시 내부 COM 객체의 인터페이스 메서드

호출

HRESULT FinalConstruct(){ hr = ::CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IAddEnd,(LPVOID*)&m_pAddEnd); if(FAILED(hr)) { MessageBox(NULL, "Creation Failed", NULL, NULL); } return hr;}

HRESULT FinalConstruct(){ hr = ::CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IAddEnd,(LPVOID*)&m_pAddEnd); if(FAILED(hr)) { MessageBox(NULL, "Creation Failed", NULL, NULL); } return hr;}

Page 133: Visual C++ ATL/COM Programming

133

ATL 의 통합 구현 (1) ATL 단순개체마법사의 옵션 페이지의 ‘집합체’에서

통합 지원 여부 결정 예 : DECLARE_AGGREGATABLE 사용 아니오 : DECLARE_NOT_AGGREGATABLE 사용 전용 : DECLARE_ONLY_AGGREGATABLE 사용

DECLARE_GET_CONTROLLING_UNKNOWN 외부 Unknown 을 구하는 GetControllingUnknown 함수 정의

FinalContruct 을 재정의하여 내부 COM 객체의 인스턴스 생성

COM 맵 수정 COM_INTERFACE_ENTRY_AGGREGATE 매크로 사용 내부 COM 객체로 인터페이스 요청을 위임

Page 134: Visual C++ ATL/COM Programming

134

ATL 의 통합 구현 (2)

BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddBack) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY_AGGREGATE(IID_IAddEnd, m_pUnk.p)END_COM_MAP()

HRESULT FinalConstruct(){ … hr = ::CoCreateInstance(clsid, GetControllingUnknown(), CLSCTX_ALL, IID_IUnknown, (LPVOID*)&m_pUnk); if(FAILED(hr)) { MessageBox(NULL, "Creation Failed", NULL, NULL); } m_pAddEnd = m_pUnk return hr;}

BEGIN_COM_MAP(CAddBack) COM_INTERFACE_ENTRY(IAddBack) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY_AGGREGATE(IID_IAddEnd, m_pUnk.p)END_COM_MAP()

HRESULT FinalConstruct(){ … hr = ::CoCreateInstance(clsid, GetControllingUnknown(), CLSCTX_ALL, IID_IUnknown, (LPVOID*)&m_pUnk); if(FAILED(hr)) { MessageBox(NULL, "Creation Failed", NULL, NULL); } m_pAddEnd = m_pUnk return hr;}

Page 135: Visual C++ ATL/COM Programming

135

COM 스레딩 모델

Page 136: Visual C++ ATL/COM Programming

136

아파트먼트 (1) COM 객체의 인스턴스와 스레드를 포함하는

논리적인 영역 아파트먼트의 기능

스레드 동시성 (Thread Concurrency) 에 대한 논리적인 구조 제공

COM 환경에서 스레드 동시성을 얻기 위해 개발자가 따라야 할 프로그래밍 규칙 제공

개발자가 COM 객체의 스레드 동시성을 관리할 수 있게 하는 시스템 코드 제공

Page 137: Visual C++ ATL/COM Programming

137

아파트먼트 (2) 모든 COM 객체는 하나의 아파트먼트에 속함 스레드는 하나의 아파트먼트에서만 실행

실행전에 아파트먼트에 들어가야 함 COM 객체는 해당 COM 객체의

아파트먼트에서 실행되는 스레드에 의해서만 접근 가능

아파트먼트의 유형 STA(Single-Threaded Apartment) MTA(Multi-Threaded Apartment)

Page 138: Visual C++ ATL/COM Programming

138

STA (1) 하나의 스레드만 실행될 수 있는 아파트먼트 STA안의 COM 객체는 동시에 여러 스레드에

의해 접근 불가 스레드 동기화를 구현할 필요 없음 STA COM 객체의 메서드 호출은 COM 에 의해 동기화

Page 139: Visual C++ ATL/COM Programming

139

STA (2)

STAProcess T1

COMobject

T1

T2

STA

COMobject

T2

허용되지 않음 (Marshaling 필요 )

Page 140: Visual C++ ATL/COM Programming

140

MTA (1) 여러 스레드가 동시에 실행될 수 있는

아파트먼트 MTA안의 COM 객체는 동시에 여러 스레드에

의해 접근 가능 MTA COM 객체는 동기화 객체를 이용하여

스레드 동기화를 구현해야 함

Page 141: Visual C++ ATL/COM Programming

141

MTA (2)

MTAProcess T1 T1

T2 T2

COMobject

동시에 접근 ( 동기화 필요 )

Page 142: Visual C++ ATL/COM Programming

142

아파트먼트 생성 HRESULT CoInitializeEx(void *pvReserved, DW

ORD dwFlags); dwFlags

COINIT_APARTMENTTHREADED : STA 생성 COINIT_MULTITHREADED : MTA 생성

HRESULT CoInitialize(void *pvReserved); HRESULT OleInitialize(void *pvReserved);

Page 143: Visual C++ ATL/COM Programming

143

인 - 프로세스 서버의스레딩 모델 표시 아웃 - 오브 - 프로세스 서버는 CoInitializeEx 를

호출하여 명확하게 어떤 아파트먼트를 사용할 지 결정

인 - 프로세스 서버의 경우 COM 객체의 CLSID 레지스트리 서브키 밑에 InprocServer32 서브키의 ThreadingModel 에 지원하는 아파트먼트 유형을 지정 키가 없는 경우 : 단일 스레딩 모델 Apartment : 아파트 스레딩 모델 Free : 자유형 스레딩 모델 Both : 모두 스레딩 모델

Page 144: Visual C++ ATL/COM Programming

144

스레딩 모델 단일 스레딩 모델 (Single Threading Model)

주 STA 에만 직접 생성 가능함 아파트 스레딩 모델 (Apartment Threading Mod

el) STA 에만 직접 생성 가능함

모두 스레딩 모델 (Mixed Threading Model) STA, MTA 에 모두 직접 생성 가능함

자유형 스레딩 모델 (Free Threading Model) MTA 에만 직접 생성 가능함

Page 145: Visual C++ ATL/COM Programming

145

인터페이스 마샬링 COM 은 마샬링을 사용하여 서로 다른

아파트먼트 영역 사이에 인터페이스 포인터를 전달

마샬링 / 언마샬링은 COM 에 의해 자동적으로 수행

아파트먼트 사이에 명확히 인터페이스 포인터를 마샬링 / 언마샬링 하는 경우 HRESULT CoMarshalInterThreadInterfaceInStream

(REFIID riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);

HRESULT CoGetInterfaceAndReleaseStream(LPSTREAM pStm, REFIID riid, LPVOID* ppv);

Page 146: Visual C++ ATL/COM Programming

146

ATL/COM 관련 레퍼런스

Page 147: Visual C++ ATL/COM Programming

147

추천서 국내서

Visual C++ ATL/COM 프로그래밍 / 전병선 / 삼양 Component Development With Visual C++ & ATL / 전병선 / 영진 .COM

COM/DCOM 프라이머 플러스 / 인포북 Inside COM+ Base Services / 정보문화사 이펙티브 COM / 워터앤오일

원서 Inside COM / Dale Rogerson Essential COM / Don Box Developer's Workshop to COM and ATL 3.0 / Troelsen ATL Internals / Rector,Sells

Page 148: Visual C++ ATL/COM Programming

148

추천 사이트 www.devpia.com www.microsoft.com/com www.codeguru.com www.codeproject.com/ MSDN

Platform SDK 의 COM and ActiveX Object Services 토픽

Books 의 Inside OLE