Visual C++ ATL Programming ( 2004/11/22~ 2005/11/26)

Download Report

Transcript Visual C++ ATL Programming ( 2004/11/22~ 2005/11/26)

Visual C++ ATL/COM
Programming
천 정 아
[email protected]
1
교육일정
1일차
COM 프로그래밍 개요
2일차
Visual C++ COM 컴파일러
3일차
ATL 개요
4일차
자동화와 이중 인터페이스
5일차
이벤트, 재사용, 스레딩 모델
2
COM 프로그래밍 개요
3
Component Object Model

자신의 고유한 기능을 제공하는 단위 어플리
케이션(즉, 컴포넌트)의 통합 및 커뮤니케이션
방법에 대한 표준을 정의한 사양


COM 컴포넌트를 정의하는 방법에 대한 표준이면
서 COM 컴포넌트를 사용하는 방법에 대한 표준임
핵심적임 MS의 기반 기술 역할 제공

OLE, ActiveX, ADO, OLE DB, Active Directory 모두
COM을 기반으로 작성
4
COM의 목표




각 컴포넌트는 서로 다른 언어로 개발될 수 있
어야 한다.
컴포넌트가 설치되고 실행하는 위치에 관계없
이 같은 방법으로 컴포넌트를 사용할 수 있어
야 한다.
컴포넌트의 버전 관리가 쉬워야 한다.
한 업체에 종속적이지 않아야 한다.
5
COM 컴포넌트의 조건




언어
이진
버전
위치
독립적이어야 한다.
형태로 제공되어야 한다.
호환성을 제공해야 한다.
투명성을 제공해야 한다.
6
COM 인터페이스 (1)


COM 객체가 자신의 기능을 노출시키는 기본
적인 방법
COM 객체와 이를 사용하는 클라이언트 사이
의 계약
COM
클라이언트
COM
컴포넌트
COM
인터페이스
7
COM 인터페이스 (2)

인터페이스의 의미

논리적 의미



물리적 의미



특정 서비스를 제공하는 일련의 함수(메서드)들
~하는 기능(서비스)
함수 포인터 배열 형태의 메모리 구조
C++의 가상함수테이블  순수 가상함수로만 구성되는
추상 클래스로 정의
각 COM 객체는 반드시 IUnknown 인터페이스
와 COM 객체 고유의 기능을 노출하는 하나 이
상의 인터페이스를 제공
8
인터페이스 메모리 구조
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 구현
};
인스턴스
인터페이스
포인터
vptr
멤버 변수
가상함수테이블
QueryInterface
AddRef
Release
Method1
Method2
Method3
9
COM 클라이언트/컴포넌트

COM 컴포넌트 : 자신의 고유한 서비스 제공

COM 서버 : 물리적인 파일(DLL, EXE)의 실행 인스
턴스


인-프로세스 서버(In-proc Server) : DLL 파일로 구현
아웃-오브-프로세스 서버(Out-of-process Server) : EXE
파일로 구현




로컬 서버(Local Server)
리모트 서버(Remote Server)
COM 객체 : COM 인터페이스를 구현한 클래스의
인스턴스
COM 클라이언트 : COM 컴포넌트의 서비스를
사용
10
GUID





Globally Unique Identifier
128 bit 크기의 정수 값(구조체로 정의)
전세계적으로 시간과 장소에 관계없이 고유하
다고 보장할 수 있는 값
UUID에서 유래
GUID의 사용



IID : 인터페이스 ID (인터페이스 식별자)
CLSID : 클래스 ID (COM 객체 식별자)
GUIDGEN.EXE로 생성
11
HRESULT



대부분의 COM 인터페이스 함수는 HRESULT
를 리턴
32 bit 정수값 (LONG)
SUCCEEDED, FAILED 매크로와 함께 사용
15 bit
16 bit
Facility code
31 30
Severity code
Return Code
16 15
0
12
COM 컴포넌트 사용
13
COM 컴포넌트 등록

레지스트리에 반드시 등록 후 사용
서버 유형
인-프로세스
서버
레지스트리
등록
Regsvr32 DLL파일명
EXE파일명 /RegServer
Regsvr32 /u DLL파일명
EXE파일명 /UnregServer
레지스트리
해제
아웃-오브-프로세스
서버
14
COM 클라이언트 작성
1.
2.
3.
4.
COM 라이브러리 초기화
COM 객체의 CLSID 구함
COM 객체의 인스턴스 생성
COM 객체가 제공하는 인터페이스 포인터를
구하여 메서드 호출 (COM 객체의 서비스 사
용)
5. COM 라이브러리의 초기화를 해제
15
COM 라이브러리 초기화/해제

COM 라이브러리


HRESULT CoInitialize(LPVOID);


프로세스마다 한번만 호출
HRESULT CoInitializeEx(LPVOID, DWORD);



COM을 사용하는 모든 application에서 유용하게 사
용될 수 있는 컴포넌트 관리 서비스 제공
#define _WIN32_DCOM 매크로 정의 후 사용
두번째 인자로 COINIT_APARTMENTTHREADED 지
정
void CoUninitialize();

CoInitialize와 짝을 이루어 호출
16
COM 객체의 CLSID 구하기
1. CLSID가 정의된 소스 파일 이용
2. 레지스트리 편집기나 OLE/COM 개체뷰어를
사용하여 CLSID 구하기
3. ProgID 사용

읽기 쉬운 문자열 형태의 식별자

<컴포넌트 명>.<객체 명>.<버전>

HKEY_CLASSES_ROOT의 서브키로 등록

사용은 간편하지만 유일성 보장되지 않음
4. 형식 라이브러리 이용
17
레지스트리 등록 예
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  ProgID
CLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95}
MediaPlayer.MediaPlayer  VerIndProgID
CurVer : MediaPlayer.MediaPlayer.1
CLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95}
18
CLSID와 ProgID 변환


HRESULT CLSIDFromProgID(LPCOLESTR,
LPCLSID);
HRESULT ProgIDFromCLSID(REFCLSID,
LPOLESTR);
19
COM의 문자열

유니코드 문자열 사용






typedef
typedef
typedef
typedef
typedef
unsigned short wchar_t;
wchar_t WCHAR;
WCHAR OLECHAR;
OLECHAR* LPOLESTR;
const OLECHAR* LPCOLESTR;
TCHAR


UNICODE 매크로 정의 시 WCHAR로 처리
UNICODE 매크로 미정의 시 char로 처리
20
COM 객체의 생성

STDAPI CoCreateInstance(REFCLSID rclsid,
LPUNKNOWN pUnkOuter, DWORD
dwClsContext, REFIID iid, LPVOID* ppv);





rclsid : COM 객체의 CLSID
pUnkOuter : 통합에서만 사용
dwClsContext : 실행 형태
(CLSCTX_INPROC_SERVER,
CLSCTX_LOCAL_SERVER 등)
riid : 사용하고자 하는 인터페이스 IID
ppv : COM 객체가 리턴하는 인터페이스 포인터의
주소
21
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;
};
22
QueryInterface


클라이언트가 COM 객체의 다른 인터페이스를
요청할 때 해당 인터페이스 포인터를 리턴
COM 객체가 제공하는 인터페이스를 구하는
유일한 방법임
23
AddRef/Release



COM 객체가 스스로 자신이 몇 번 참조되어
있는가 하는 횟수를 관리
레퍼런스 카운터가 0일 때 스스로를 소멸
참조 카운터 규칙
1. 인터페이스를 리턴하기 전에 AddRef 한다.
2. 인터페이스의 사용이 끝나면 Release 한다.
3. 인터페이스 포인터를 다른 인터페이스 포인터에
대입할 때도 AddRef 한다.
24
COM 객체의 사용




인터페이스 포인터를 통하여 인터페이스가 제
공하는 메서드 호출
QueryInterface를 통하여 COM 객체가 지원하
는 다른 인터페이스를 요청
인터페이스의 사용이 끝나면 Release 호출
COM 서버가 할당한 메모리를 클라이언트가
해제해야 하는 경우 CoTaskMemAlloc,
CoTaskMemFree 사용
25
COM 컴포넌트 구현
26
COM 컴포넌트의 구현
1.
2.
3.
4.
COM 인터페이스의 정의
COM 객체 클래스 구현
클래스 팩토리 클래스 구현
COM 서버 구현
27
COM 인터페이스 정의

IDL로 COM 인터페이스 정의


MIDL 컴파일러로 컴파일
IDL(Interface Definition Language)






인터페이스를 정의하는 표준 개발 도구
MIDL 컴파일러 제공
OSF RPC의 IDL을 확장
C/C++ like language with attribute
언어 독립성 제공 (형식 라이브러리)
위치투명성 제공 (프록시/스텁 코드)
28
IDL의 예
// 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);
};

인터페이스 헤더


object : COM 인터페이스
uuid : 인터페이스 식별자
(IID)

메서드의 인자 헤더



in : 클라이언트에서 서버로
이동(마샬링)
out : 서버에서 클라이언트로
이동(마샬링)
string : NULL 종료 문자열
29
MIDL 컴파일러의 역할



C/C++에서 사용할 수 있는 인터페이스를 정
의한 코드를 포함하는 헤더 파일 생성
커스텀 인터페이스에 대한 프록시(proxy)/스
텁(stub) 코드 생성
자동화에서 사용되는 형식 라이브러리(Type
Library) 생성

library, coclass 문이 사용되는 경우에만 생성
30
COM 객체 구현

인터페이스 포함




COM 객체 클래스 안에 인터페이스 구현 클래스를
포함
구문은 복잡하지만 디버깅이 쉽다.
MFC에서 사용
인터페이스 상속



COM 객체 클래스를 인터페이스에서 상속
간단하다.
ATL에서 사용
31
COM 객체 클래스 정의


다중 인터페이스 구현을 위해 다중 상속 이용
COM 객체 구현 클래스에서는 모든 인터페이
스 메서드를 재정의해야 한다.
class CHello : public IHello, public IGoodbye
{
// IUnknown 메서드 구현
// IHello 메서드 구현
// IGoodbye 메서드 구현
};
32
다중 상속 시 메모리 구조
CHello::this
(IHello*)CHello::this
(IGoodbye*)CHello ::this
IHello vptr
IGoodbye vptr
멤버 변수
QueryInterface
AddRef
Release
sayHello
IHello
QueryInterface
AddRef
Release
sayGoodbye
IGoodbye
33
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;
34
AddRef, Release의 구현

InterlockedIncrement, InterlockedDecrement
함수

thread-safe하게 증가, 감소
ULONG _stdcall CHello::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG _stdcall CHello::Release()
{
if(InterlockedDecrement(&m_cRef) == 0) {
delete this;
}
return m_cRef;
}
35
COM 객체 서비스 메서드 구현



COM 객체 고유한 서비스를 제공하는 인터페
이스의 메소드를 구현
필요 시 생성자, 소멸자, 멤버 변수 추가 가능
COM 객체의 CLSID 정의

GUIDGEN 이용
36
클래스 팩토리의 구현


COM 컴포넌트는 COM 객체의 인스턴스를 생
성할 수 있는 매커니즘 제공해야 함
클래스 팩토리가 COM 객체의 인스턴스를 생
성


클래스 팩토리도 일종의 COM 객체
IClassFactory를 반드시 제공해야 함
37
IClassFactory
interface IClassFactory : IUnknown {
HRESULT __stdcall CreateInstance(
LPUNKNOWN pUnkOuter,
REFIID iid,
LPVOID* ppv) = 0;
HRESULT __stdcall LockServer(BOOL bLock) = 0;
};
38
컴포넌트의 생성 과정
Client
COM Library
CoGetClassObject 호출
CoGetClassObject
Server
DllGetClassObject
클래스
팩토리
생성
IClassFactory 리턴
pIClassFactory
IClassFactory::CreateInstance 호출
CFactory
IHello 리턴
pIHello::sayHello호출
pIHello
pIClassFactory->Release() 호출
CHello
컴포넌트
생성
39
컴포넌트 생성 과정 (코드)
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;
}
40
인-프로세스 서버의 구현


클라이언트에서 COM 객체 사용 시 호출되는
4개의 익스포트 함수 구현
DllGetClassObject


DllRegisterServer, DllUnregisterServer


CoGetClassObject에 의해 호출
REGSVR32.EXE에 의해 호출
DllCanUnloadNow

CoFreeUnusedLibrary에 의해 호출
41
로컬 서버 구현


Win32에서는 프로세스마다 주소공간이 다르
므로 로컬 서버나 리모트 서버가 리턴한 인터
페이스 포인터는 클라이언트 주소공간에서는
의미가 없다.
위치 투명성을 제공하기 위해서는 프록시와
스텁이 필요
42
프록시(Proxy)/스텁(Stub)
Process Boundary
Network Boundary
Client
EXE
Server
EXE
Proxy
DLL
Stub
DLL
마샬링
LPC
RPC
언마샬링
43
마샬링
1. 표준 인터페이스의 마샬링

운영체제에 의해 제공(ole32.dll)
2. 자동화 인터페이스의 마샬링

운영체제에 의해 제공(oleaut32.dll)
3. 커스텀 인터페이스의 마샬링


MIDL 컴파일 결과로 만들어지는 코드 이용
IDL 작성 시 속성 지정
44
로컬 서버의 구현


인-프로세스 서버와는 달리 CoInitialize,
CoUninitialize 호출
DllRegisterServer, DllUnregisterServer



DllGetClassObject


명령행 인자 처리로 구현
/RegServer, /UnregServer 옵션 처리
CoRegisterClassObject, CoRevokeClassObject 이용
DllCanUnloadNow

스스로 능동적으로 종료
45
CoRegisterClassObject (1)


COM은 내부적으로 등록된 클래스 팩토리
COM 객체를 저장하는 ROT 관리
클라이언트가 CoGetClassObject 호출 시 ROT
부터 검사함


등록이 안된 경우 /Embedding 옵션으로 로컬 서버
실행
클래스 팩토리 COM 객체를 ROT에 등록함
46
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에서 사용)
47
로컬 서버 종료

다음 조건 만족 시 능동적으로 종료



COM 객체 카운터가 0이고 클라이언트가
IClassFactory::LockServer(FALSE)를 호출함으로써
마지막 로크 카운터가 0이 될 때
로크 카운터가 현재 0이고, 클라이언트가
IUnknown::Release를 호출하여 마지막 COM 객체
카운터가 0이 될 때
PostQuitMessage 함수 이용
48
VC++ COM 컴파일러
49
VC++의 COM 지원







#import
__declspec 확장 속성 : uuid, property
__uuidof
_com_ptr_t 클래스
_com_error 클래스
_bstr_t 클래스
_variant_t 클래스
50
형식 라이브러리






COM 컴포넌트가 노출하는 COM 객체에 대한
정보를 포함하는 일종의 복합 도큐먼트(*.tlb)
COM 객체에 대한 매뉴얼
IDL의 library 문과 coclass 문에 의해 제공
DLL이나 EXE 파일에 리소스로 포함 가능 (리
소스 스크립트에 선언)
형식 라이브러리 자체도 ITypeLib, ITypeLib2,
ITypeInfo, ITypeInfo2, ITypeComp 인터페이
스를 노출하는 하나의 COM 객체임
HKEY_CLASSES_ROOT\TypeLib 서브키에 저장
51
#import



#import “파일이름” [속성 리스트]
#import “progid:XXX.XXX.XXX” [속성 리스트]
#import “libid:XXXXXX” [속성 리스트]


속성 리스트 : no_namespace, named_guids
2개의 C++ 소스 코드 생성


*.tlh : 스마트 포인터 클래스, 인터페이스의 정의
포함
*.tli : COM 객체의 메서드를 호출하는 구현 코드
포함(래퍼 함수)
52
__declspec(uuid())
__uuidof()

COM 객체나 인터페이스에 GUID 지정
struct __declspec(uuid(“60bb225d-a06f-11d1-9138
-0020af715af0”)) Hello;
struct __declspec(uuid(“60bb225b-a06f-11d1-9138
-0020af715af0”)) IHello;

__uuidof() : 클래스 명이나 인터페이스 명에 대한
GUID 값 리턴


__uuidof(Hello);
__uuidof(IHello);
53
스마트 포인터


인터페이스 포인터를 관리해주는 클래스
operator-> 함수를 제공
54
_com_ptr_t 클래스 (1)



스마트 포인터 기능을 제공
CoCreatInstance, AddRef, Release,
QueryInterface 호출 자동화
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;
55
_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();
56
_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());
}
57
_com_error 클래스 (2)

멤버함수




Error : HRESULT 값 리턴
ErrorInfo : IErrorInfo 인터페이스 리턴
Description, HelpContext, HelpFile, Source, GUID :
IErrorInfo 메서드 제공
ErrorMessage : HRESULT에 대한 문자열 형태의 에
러 메시지 리턴
58
_bstr_t, _variant_t 클래스

_bstr_t



BSTR를 캡슐화
SysAllocString과 SysFreeString 호출
_variant_t


VARIANT를 캡슐화
VariantInit과 VariantClear 호출
59
ATL 프로그래밍 기초
60
Active Template Library

ATL의 목적


작고 빠르고 확장성을 갖는 COM 객체를 손쉽게 구
현 (IUnknown, IClassFactory의 구현 제공)
MFC vs ATL

MFC



풍부한 사용자 인터페이스 기능 제공
MFC 디폴트 프레임워크  크기, 속도 문제
ATL


프레임워크 불필요. 실행 라이브러리 불필요  작고 빠
른 COM 컴포넌트 생성에 적합
업무 로직을 구현하는 자동화 컴포넌트 구현에 적합
61
C++의 템플릿 (1)



범용형에 의해 함수/클래스 템플릿을 정의
템플릿의 인자에 의해 함수나 클래스 코드가
생성
꼭 필요한 코드만 생성  작고 효율적인 코드
template <typename T> class CStack {
private:
T* p;
int sz;
public:
void Push(T a) { *p++ = a; }
…
};
CStack <int> si(10);
62
C++의 템플릿 (2)
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;
63
ATL 프로젝트 마법사 (1)


서버 유형에 따라 ATL 프로젝트에 필요한 골격 코드
와 프로젝트 파일 생성
서버 유형

동적 연결 라이브러리(DLL)



실행 파일(EXE)



인-프로세스 서버 COM 컴포넌트 생성
DllMain, DllGetClassObject, DllCanUnloadNow, DllRegisterServer,
DllUnregisterServer의 구현 코드 제공
아웃-오브-프로세스 서버 COM 컴포넌트 생성
WinMain안에 레지스트리 등록 및 해제, ROT에 클래스 팩토리
등록 및 해제, 메시지 루프 등 기본 코드 제공
서비스(Service)

Windows NT 서비스 생성
64
ATL 프로젝트 마법사 (2)

추가 옵션

프록시/스텁 코드 병합 허용




COM 컴포넌트와 같은 DLL안에 프록시/스텁 코드 추가
MFC 지원
COM+ 1.0 지원
특성 사용 (ATL 7.0)


attribute 기반 프로그래밍 지원 기능 추가
좀더 쉽게 COM 프로그래밍을 할 수 있도록
attribute 제공  필요한 코드 생성
65
서버 모듈 클래스(ATL 3.0)

CComModule 클래스




COM 서버 모듈 구현
클라이언트에서 COM 서버내의 COM 객체로 접근
하는 기능 제공
인-프로세스 서버와 아웃-오브-프로세스 서버를 모
두 지원
객체 맵(Object Map)을 사용하여 COM 서버 모듈에
포함된 COM 객체 관리
// COM 서버 모듈 전역 객체 정의
CComModule _Module;
66
서버 모듈 클래스(ATL 3.0)

CComModule 멤버








Init : 멤버 변수 초기화
Term : 멤버 변수 해제
GetClassObject : 클래스 팩토리 생성 (DLL 전용)
GetLockCount : 로크 카운터 리턴
RegisterServer : 레지스트리에 객체 맵에 정의된
COM 객체 등록
UnregisterServer : 레지스트리에서 객체 맵에 정의
된 COM 객체의 등록 해제
RegisterClassObjects : ROT에 클래스 팩토리 등록
(EXE 전용)
RevokeClassObjects : ROT에서 클래스 팩토리의
등록 해제 (EXE 전용)
67
서버 모듈 클래스(ATL 7.0)

인-프로세스 서버와 아웃-오브-프로세스 서버
의 기능을 나눠서 제공할 수 있도록 계층 구조
정의
ATL_MODULE
CAtlModule
CAtlModuleT
CAtlDllModuleT
CAtlExeModuleT
68
객체 맵(Object Map)



ATL 단순 개체 마법사로 COM 객체를 생성할
때마다 객체 맵에 생성된 COM 객체에 대한 정
보 추가
_ATL_OBJMAP_ENTRY 구조체 배열
객체 맵의 정보



레지스트리에 COM 객체 등록 및 해제 방법
클래스 팩토리 생성 방법
COM 객체 생성 방법
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_Hello, CHello)
END_OBJECT_MAP()
69
객체 맵(Object Map) (ATL 7.0)

ATL 7.0에서는 별도의 객체 맵을 따로 관리하
는 대신 OBJECT_ENTRY_AUTO 매크로 사용


OBJECT_ENTRY_AUTO 매크로는 COM 서버 내에
COM 객체를 추가할 때마다 COM 객체의 구현 클래
스 헤더 파일에 함께 생성됨
링크 단계에서 객체 맵을 링커가 하나로 합쳐줌
70
ATL 단순 개체 마법사



COM 서버에 COM 객체 추가
COM 객체의 카테고리와 유형 선택
이름 페이지


약식 이름 : COM 객체명 입력
속성 페이지




스레딩 모델
인터페이스
집합체
지원
71
ATL 기초 클래스

ATL 단순 개체 마법사가 생성하는 COM 객체
클래스의 구현
class ATL_NO_VTABLE CHello :
public CComObjectRootEx<CComSingleThreadingModel>,
public CComCoClass<CHello, &CLSID_Hello>,
public IHello
{
…
};
72
ATL COM 객체의 계층 구조
CComObjectRootEx
CComCoClass
CHello
CCom(Agg)Object
COM 객체의
구현클래스
 추상클래스
COM 객체의
인스턴스 생성시
사용되는 클래스
73
CComObjectRootEx


IUnknown 인터페이스의 내부 구현 제공
스레딩 모델에 따른 레퍼런스 카운트 구현



CComSingleThreadingModel
CComMultiThreadingModel
멤버 함수



InternalQueryInterface, InternalAddRef,
InternalRelease : IUnknown의 내부 구현
OuterQueryInterface, OuterAddRef, OuterRelease :
통합시 외부 COM 객체로 IUnknown을 위임하는 코
드 구현
FinalConstruct, FinalRelease : COM 객체의 초기화,
해제를 위한 가상 함수
74
CComCoClass (1)

DECLARE_CLASSFACTORY() 매크로


COM 객체의 디폴트 클래스 팩토리 정의
CComClassFactory


IClassFactory의 구현 제공
CComCreator

클래스 팩토리 COM 객체의 인스턴스 생성
DECLARE_CLASSFACTORY()
 확장후
typedef CComCreator< CComObject< CComClassFactory > >
_ClassFactoryCreatorClass;
75
CComCoClass (2)

DECLARE_AGGREGATABLE() 매크로

COM 객체가 통합되어 사용될 수 있음


통합되지 않을 때는 CComObject 클래스를 사용
통합될 때는 CComAggObject 클래스 사용
DECLARE_AGGREGATABLE(CHello)
 확장후
typedef CComCreator2< CComCreator< CComObject< CHello > >,
CComCreator< CComAggObject< CHello > > >
_CreatorClass;
76
CCom(Agg)Object



사용자 COM 객체 클래스를 템플릿의 인자로
받아 사용자 COM 객체 클래스를 상속받는 클
래스로 정의됨
QueryInterface, AddRef, Release의 구현 제공
실제 COM 객체의 인스턴스 생성 시 이용되는
클래스임
template <class Base>
class CComObject : public Base
{
…
};
77
ATL_NO_VTABLE



__declspec(novtable)로 확장
클래스의 생성자와 소멸자에서 가상함수 테이
블과 vptr가 초기화되는 것을 막아줌
사용자 COM 객체 클래스는 추상클래스 이므
로 가상함수 테이블이나 vptr을 초기화할 필요
가 없음  파생클래스(CComObject)에서 처
리됨
78
COM Map



COM 객체 클래스 선언부에 COM 맵 정의
COM 맵을 사용하여 COM 객체가 제공하는 각
인터페이스를 관리
CComObjectRootEx::InternalQueryInterface의
구현에서 COM 맵 정보를 사용
BEGIN_COM_MAP(CHello)
COM_INTERFACE_ENTRY(IHello)
END_COM_MAP()
79
Property, Method 추가

속성(Property) 추가 마법사



클래스뷰에서 ‘속성 추가’ 선택
속성 형식, 속성 이름 지정
함수 형식




Get 함수 : 읽기 함수
Put 함수 : 쓰기 함수
COM 객체 구현 클래스에 멤버 변수 추가 (초기화)
메서드(Method) 추가 마법사

클래스뷰에서 ‘메서드 추가’ 선택
80
새로운 인터페이스의 추가

직접 수정하는 경우





IDL 파일에 인터페이스 정의
COM 객체 클래스가 인터페이스를 상속받도록 수
정
COM 맵에 인터페이스 추가
인터페이스의 메서드 구현
인터페이스 구현 마법사를 사용하는 경우



IDL 파일에 인터페이스 정의
MIDL 컴파일
클래스뷰에서 ‘인터페이스 구현’ 선택
81
CComPtr, CComQIPtr




AddRef, Release 호출 자동화
CComQIPtr은 QueryInterface 호출 자동화
_com_ptr_t와는 달리 인스턴스 생성 기능 없
음
CComPtr은 다른 인터페이스 수용 불가
CComPtr<IUnknown> pUnk;
CComPtr<IHello> pHello1;
CComPtr<IHello> pHello2(pUnk); // 에러
82
CComBSTR, CComVariant

CComBSTR

주요멤버




m_str : BSTR 값
Append, AppendBSTR, Copy, Empty, Length
operator BSTR, =, +=, &, !
CComVariant

주요 멤버


ChangeType, Clear, Copy
operator =, ==, !=
83
ATL Debugging

_ATL_DEBUG_INTERFACES



_ATL_DEBUG_QI



<atlcom.h>앞에 정의
QueryInterface 디버깅
ATLTRACE(…)


<atlbase.h>앞에 정의
Reference Counting과 QueryInterface 디버깅
Output 윈도우에 디버깅 정보 출력
_ATL_MIN_CRT

CRT 라이브러리 제거(Release 모드)
84
자동화와 이중 인터페이스
85
자동화(Automation)


어플리케이션이 자신의 기능을 다른 어플리케
이션에 노출하는 기능을 자동화라고 함
자동화 컴포넌트, 자동화 서버


자동화 컨트롤러, 자동화 클라이언트


자동화 서비스를 제공하는 어플리케이션
자동화 서비스를 사용하는 어플리케이션
자동화 객체


자동화 서비스를 제공하는 객체
메서드(Method), 속성(Property), 이벤트(Event)를
통해 기능 제공
86
자동화의 특징

스크립트 언어에서는 COM 객체를 사용할 수
없음



가상함수 테이블을 인식할 수 없음
자료형 개념이 불분명함
이를 해결하기 위한 방법이 자동화 기술


가상함수 테이블 인식 문제  IDispatch로 해결
자료형 문제  자동화 데이터 타입으로 해결
87
자동화의 장단점

장점



스크립트 클라이언트에서도 자동화 객체 사용 가
능
마샬링 코드를 운영체제가 제공(oleaut32.dll)
단점


실행 속도가 저하됨
사용할 수 있는 데이터형이 제한됨
88
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);
};
89
자동화의 구현

디스패치 인터페이스(dispinterface)


Invoke 함수는 DISPID를 함수 포인터 배열의 인덱
스로 사용하여 함수 호출

COM 인터페이스가 아님  불변성 X

메서드가 HRESULT를 리턴할 필요 없음
이중 인터페이스(dual interface)


dispinterface의 속도 저하 문제 개선
IDispatch를 상속받는 COM interface를 사용  속
도 개선 효과
90
후기 바인딩 vs 초기 바인딩

후기 바인딩





실행 시간에 DISPID 결정
속도가 느리며 잘못된 속성명이나 메서드명 사용
시 실행 에러 발생
컴파일 시 데이터 형 검사 X  실행 에러 발생
실행 시간에 자동화 객체의 기능 결정
초기 바인딩



ID 바인딩
형식 라이브러리에서 DISPID 정보를 읽어서 사용
 후기 바인딩에 비해 속도가 빠름
컴파일 시 데이터 형 검사 가능
91
VARIANT 데이터형


서로 다른 데이터형을 멤버로 갖는 16 byte 크
기의 공용체(union)
VARIANT 관련 함수


VariantInit : VARIANT 구조체 초기화
VariantChangeType : 데이터형 변환
92
BSTR, SAFEARRAY

BSTR



byte-length prefixed unicode string
SysAllocString, SysFreeString 이용
SAFEARRAY


배열 범위에 대한 정보를 포함하고 있는 배열
SafeArray… 함수 이용
93
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()
};
94
MFC 자동화 클라이언트

‘클래스 추가’에서 ‘TypeLib의 MFC 클래스’로
자동화 객체에 대한 wrapper class 생성 
CAddBack



COleDispatchDriver 의 파생 클래스
IDispatch를 통한 메서드, 속성 호출을 처리하는 멤
버 함수 제공
자동화 객체 생성시
COleDispatchDriver::CreateDispatch 이용

ProgID나 CLSID로 자동화 객체 생성
95
Multiple Dual Interface (1)

여러 개의 dual interface를 갖는 자동화 객체
구현 가능
[…, dual, …]
interface IAddEnd : IDispatch
{…
};
[…, dual, …]
interface IAdd : IDispatch
{…
};
[…]
library ADDBACKLib
{
[…]
coclass AddBack
{
[default] interface IAddEnd;
interface IAdd;
};
};
96
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()
};
97
DISPID 인코딩


multiple dual interface를 갖는 자동화 객체의
모든 기능을 제공하기 위한 방법
COM 객체 구현 클래스에 GetIDsOfNames,
Invoke를 재정의


GetIDsOfNames : DISPID를 찾아 인터페이스 정보
를 함께 인코딩하여 리턴
Invoke : 넘겨받은 DISPID를 디코딩하여 어느 인터
페이스의 함수를 호출할 지 결정
인코딩된 DISPID (4 byte)
DISPID
인터페이스 구분
2 byte
2 byte
98
에러 정보
ISupportErrorInfo
자동화
클라이언트
ISupportErrorInfo
구함
 InterfaceSupportsErrorInfo
호출하여 에러 정보 제공
여부 확인
GetErrorInfo로
IErrorInfo구함
자동화
객체
CreateErrorInfo
(에러정보객체 생성)
ICreateErrorInfo로
에러 정보 지정
SetErrorInfo
(현재 스레드와
에러정보객체연결)
ICreateErrorInfo
IErrorInfo로
에러 정보 추출
IErrorInfo
에러 정보
객체
 에러정보객체의 IErrorInfo 구함
99
에러 정보 리턴 (1)


자동화 객체에서는 IErrorInfo 객체를 생성하
여 여기에 발생한 에러에 대한 자세한 정보를
제공
자동화 객체는 ISupportErrorInfo 인터페이스
를 지원해야 함


ATL 단순개체마법사의 옵션 페이지의
‘ISupportErrorInfo’ 지원 선택
InterfaceSupportsErrorInfo : 해당 인터페이스가 자
세한 에러 정보를 제공하는 지 여부를 결정
100
에러 정보 리턴 (2)

에러 정보 제공


CreateErrorInfo 함수를 호출하여 에러 정보 객체
생성
ICreateErrorInfo 인터페이스로 에러 정보 저장






SetSource : 에러가 발생한 자동화 객체명 지정(ProgID)
SetDescription : 에러에 대한 설명 텍스트 지정
SetGUID : 에러를 정의한 인터페이스의 GUID 지정
SetHelpFile : 에러를 설명하는 도움말파일의 경로명 지정
SetHelpContext : 에러에 대한 도움말 컨텍스트 ID 지정
SetErrorInfo 함수를 호출하여 에러 정보 객체를 현
재의 스레드와 연결
101
에러 정보 리턴 (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;
102
에러 정보 리턴 (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;
}
103
에러 정보 받기

자동화 객체에서 에러 정보를 받는 방법


IDispatch::Invoke의 EXCEPINFO 인자 사용
직접 에러 정보 객체를 사용


에러 발생시 ISupportErrorInfo 인터페이스를 구하여
InterfaceSupportsErrorInfo 함수를 호출  IErrorInfo를
지원하는지 여부 확인
GetErrorInfo를 호출하여 IErrorInfo 인터페이스를 구한
후 IErrorInfo 인터페이스의 메서드를 호출하여 에러 정보
를 추출

GetSource, GetDescription, GetGUID, GetHelpFile,
GetHelpContext 등 이용
104
자동화 객체의 안전성 보장 (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()
};
105
자동화 객체의 안전성 보장 (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()
};
106
이벤트 구현
107
이벤트


자동화 객체가 자신에게 어떠한 사건이 발생
했다는 사실을 자동화 컨트롤러에게 알려주는
기능
커넥션 포인트 메커니즘으로 구현
Property/Method
자동화
컨트롤러
Event
자동화
객체
108
커넥션 포인트 (1)

소스 객체(source object)



싱크 오브젝트



이벤트를 발생(fire)시키는 소스 인터페이스(source
interface, outgoing interface)를 제공
자동화 객체가 소스 객체가 됨
이벤트를 받아들이는 싱크 인터페이스(sink
interface, incoming interface)를 제공
자동화 컨트롤러 내에 별도로 생성
이벤트


자동화 객체가 호출하고 싱크 객체가 구현하는 싱
크 인터페이스의 메서드
싱크 인터페이스와 소스 인터페이스는 서로 일치
109
커넥션 포인트 (2)






자동화 컨트롤러는 싱크 객체 생성
자동화 컨트롤러는 자동화 객체에게
IConnectionPointContainer 요청
IConnectionPointContainer::FindConnectionPoint 호출
 소스 인터페이스에 대한 IConnectionPoint 구함
IConnectionPoint::Advise를 호출하여 싱크 객체의 인
터페이스를 넘겨줌
자동화 객체와 자동화 컨트롤러 연결  자동화 객체
는 필요할 때 이벤트를 발생(fire)시킴
IConnectionPoint::Unadvise를 호출하여 커넥션 포인
트를 단절
110
소스 인터페이스의 정의 (1)



[
이중 인터페이스로 정의
dispinterface로 정의  스크립트 클라이언트
에서 사용
COM 객체에 추가 시 source 속성 지정
object,
uuid(D90D5240-ACD0-11d1-913A-0020AF715AF0),
dual,
helpstring("IAddBackEvent Interface"),
pointer_default(unique)
]
interface IAddBackEvent : IDispatch
{
};
111
소스 인터페이스의 정의 (2)
[
uuid(B086DCA0-ACD9-11d1-913A-0020AF715AF0),
nonextensible
]
dispinterface _IAddBackEvent
{
properties:
methods:
};
…
coclass AddBack
{
[default] interface IAddBack;
[default, source] dispinterface _IAddBackEvent;
[source] interface IAddBackEvent;
};
112
커넥션 포인트 클래스 생성



MIDL 컴파일
클래스뷰의 ‘연결 지점 추가’ 사용
커넥션 포인트 클래스 생성
template <class T>
class CProxyIAddBackEvent : public IConnectionPointImpl<T,
&IID_IAddBackEvent, CComDynamicUnkArray>
{
};
template <class T>
class CProxy_IAddBackEvent : public IConnectionPointImpl<T,
&DIID__IAddBackEvent, CComDynamicUnkArray>
{
};
113
자동화 객체 클래스 수정

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>
{
…
};
114
커넥션 포인트 맵

IConnectionPointContainer::FindConnectionPoint가 소
스 인터페이스에 대한 IConnectionPoint 인터페이스를
찾는 방법 제공
BEGIN_CONNECTION_POINT_MAP(CAddBack)
CONNECTION_POINT_ENTRY(IID_IAddBackEvent)
CONNECTION_POINT_ENTRY(DIID__IAddBackEvent)
END_CONNECTION_POINT_MAP()
115
이벤트 발생

이벤트를 발생하려면 커넥션 포인트 클래스에
정의된 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;
}
116
자동화 컨트롤러의
이벤트 받기


싱크 객체의 구현 필요  ATL 이용
MFC 어플리케이션에서의 ATL 사용


ATL 헤더 포함
CComModule의 전역 오브젝트 정의

초기화 및 해제 필요
// 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();
}
117
싱크 객체 구현

tlh 파일에 정의된 소스 인터페이스 메서드 구현
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);
118
싱크 객체 생성

싱크 객체 생성 후 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("이벤트를 받을 수 없습니다.");
…
119
싱크 객체 소멸

자동화 객체 사용후 AtlUnadvise 호출  연결
해제
void CAddFrontDlg::~CAddFrontDlg()
{
IUnknownPtr pUnk = m_pAddBack;
AtlUnadvise(pUnk, IID_IAddBackEvent, m_Cookie); // 연결 해제
}
m_pAddBack = 0;
120
dispinterface
싱크 객체 구현 (1)

싱크 인터페이스




이중 인터페이스 또는 dispinterface로 구현
이중 인터페이스를 구현하는 싱크 객체의 경우 모
든 이벤트를 구현해야 함
dispinterface를 구현하는 싱크 객체의 경우 모든
이벤트를 구현할 필요가 없음
이벤트 싱크 구현을 위한 IDispEventImpl<>
클래스 사용
121
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()
122
dispinterface
싱크 객체 생성

IDispEventImpl::DispEventAdvise 호출  소스 연결
BOOL CAddFrontDlg::OnInitDialog()
{
…
IUnknownPtr pUnk = m_pAddBack;
m_pEventHandler = new CEventHandler;
HRESULT hr = m_pEventHandler->DispEventAdvise(pUnk);
if(FAILED(hr))
AfxMessageBox("이벤트를 받을 수 없습니다.");
…
123
dispinterface
싱크 객체 소멸

IDispEventImpl::DispEventUnadvise 호출  연결 해제
void CAddFrontDlg::~CAddFrontDlg()
{
IUnknownPtr pUnk = m_pAddBack;
m_pEventHandler->DispEventUnadvise(pUnk);
delete m_pEventHandler;
}
m_pAddBack = 0;
124
스크립트 클라이언트의
싱크 객체 구현


스크립트 클라이언트는 실행 시간에 동적으로
싱크 객체 구현
이 때 싱크 인터페이스 정보를 얻기 위해 자동
화 객체의 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()
};
125
COM 객체의 재사용
126
COM의 재사용성


COM은 구현 상속은 제공하지 않음  인터페이스 상
속만을 제공
이진 레벨의 재사용 기법


포함(Containment)
통합(Aggregation)
Outer
Inner
127
포함 (1)


C++의 포함과 유사한 개념
외부 COM 객체는 내부 COM 객체를 이용하여
자신의 인터페이스를 구현




외부 COM 객체는 내부 COM 객체의 인터페이스 함
수를 단순히 호출  Forwarding 가능
외부 COM 객체는 내부 COM 객체의 인터페이스 함
수 호출에 다른 기능을 추가  Adding 가능
외부 COM 객체가 내부 COM 객체의 생성과 소
멸을 모두 직접 관리
모든 COM 객체가 포함 가능
128
포함 (2)
IAddBack
IAddEnd
CLSID_AddBack
m_pAddEnd
CAddBack
IAddEnd
CoCreateInstance(
CLSID_AddEnd,…);
CAddEnd
CLSID_AddEnd
129
통합 (1)


포함의 특별한 경우로 볼 수 있음
외부 COM 객체가 내부 COM 객체의 인터페이
스를 직접 클라이언트에게 제공





외부 COM 객체는 인터페이스를 구현하지 않음
Forwarding O, Adding X
외부 COM 객체는 내부 COM 객체 생성 시 자
신의 IUnknown을 제공
외부 COM 객체는 내부 COM 객체 접근 시 내
부 COM 객체의 IUnknown으로만 접근
통합을 지원하는 COM 객체만 통합 가능
130
통합 (2)
IAddBack
IAddEnd
CLSID_AddBack
m_pUnk
CAddBack
CoCreateInstance(
CLSID_AddEnd,
(IUnknown*)this,
CLSCTX_ALL,
IID_IUnknown,
(void**)&m_pUnk);
CAddEnd
CLSID_AddEnd
131
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;
}
132
ATL의 통합 구현 (1)

ATL 단순개체마법사의 옵션 페이지의 ‘집합체’에서
통합 지원 여부 결정




DECLARE_GET_CONTROLLING_UNKNOWN



예 : DECLARE_AGGREGATABLE 사용
아니오 : DECLARE_NOT_AGGREGATABLE 사용
전용 : DECLARE_ONLY_AGGREGATABLE 사용
외부 Unknown을 구하는 GetControllingUnknown 함수 정의
FinalContruct을 재정의하여 내부 COM 객체의 인스턴
스 생성
COM 맵 수정


COM_INTERFACE_ENTRY_AGGREGATE 매크로 사용
내부 COM 객체로 인터페이스 요청을 위임
133
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;
}
134
COM 스레딩 모델
135
아파트먼트 (1)


COM 객체의 인스턴스와 스레드를 포함하는
논리적인 영역
아파트먼트의 기능



스레드 동시성(Thread Concurrency)에 대한 논리
적인 구조 제공
COM 환경에서 스레드 동시성을 얻기 위해 개발자
가 따라야 할 프로그래밍 규칙 제공
개발자가 COM 객체의 스레드 동시성을 관리할 수
있게 하는 시스템 코드 제공
136
아파트먼트 (2)




모든 COM 객체는 하나의 아파트먼트에 속함
스레드는 하나의 아파트먼트에서만 실행 
실행전에 아파트먼트에 들어가야 함
COM 객체는 해당 COM 객체의 아파트먼트에
서 실행되는 스레드에 의해서만 접근 가능
아파트먼트의 유형


STA(Single-Threaded Apartment)
MTA(Multi-Threaded Apartment)
137
STA (1)




하나의 스레드만 실행될 수 있는 아파트먼트
STA안의 COM 객체는 동시에 여러 스레드에
의해 접근 불가
스레드 동기화를 구현할 필요 없음
STA COM 객체의 메서드 호출은 COM에 의해
동기화
138
STA (2)
Process
T1
T1
STA
COM
object
T2
허용되지 않음(Marshaling 필요)
T2
STA
COM
object
139
MTA (1)



여러 스레드가 동시에 실행될 수 있는 아파트
먼트
MTA안의 COM 객체는 동시에 여러 스레드에
의해 접근 가능
MTA COM 객체는 동기화 객체를 이용하여 스
레드 동기화를 구현해야 함
140
MTA (2)
Process
T1
T1
MTA
COM
object
동시에 접근(동기화 필요)
T2
T2
141
아파트먼트 생성

HRESULT CoInitializeEx(void *pvReserved,
DWORD dwFlags);

dwFlags




COINIT_APARTMENTTHREADED : STA 생성
COINIT_MULTITHREADED : MTA 생성
HRESULT CoInitialize(void *pvReserved);
HRESULT OleInitialize(void *pvReserved);
142
인-프로세스 서버의
스레딩 모델 표시


아웃-오브-프로세스 서버는 CoInitializeEx를
호출하여 명확하게 어떤 아파트먼트를 사용할
지 결정
인-프로세스 서버의 경우 COM 객체의 CLSID
레지스트리 서브키 밑에 InprocServer32 서브
키의 ThreadingModel에 지원하는 아파트먼트
유형을 지정




키가 없는 경우 : 단일 스레딩 모델
Apartment : 아파트 스레딩 모델
Free : 자유형 스레딩 모델
Both : 모두 스레딩 모델
143
스레딩 모델

단일 스레딩 모델(Single Threading Model)


아파트 스레딩 모델(Apartment Threading
Model)


STA에만 직접 생성 가능함
모두 스레딩 모델 (Mixed Threading Model)


주 STA에만 직접 생성 가능함
STA, MTA에 모두 직접 생성 가능함
자유형 스레딩 모델(Free Threading Model)

MTA에만 직접 생성 가능함
144
인터페이스 마샬링



COM은 마샬링을 사용하여 서로 다른 아파트
먼트 영역 사이에 인터페이스 포인터를 전달
마샬링/언마샬링은 COM에 의해 자동적으로
수행
아파트먼트 사이에 명확히 인터페이스 포인터
를 마샬링/언마샬링 하는 경우


HRESULT
CoMarshalInterThreadInterfaceInStream(REFIID
riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);
HRESULT
CoGetInterfaceAndReleaseStream(LPSTREAM
pStm, REFIID riid, LPVOID* ppv);
145
ATL/COM 관련 레퍼런스
146
추천서

국내서






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
147
추천 사이트





www.devpia.com
www.microsoft.com/com
www.codeguru.com
www.codeproject.com/
MSDN


Platform SDK의 COM and ActiveX Object Services
토픽
Books의 Inside OLE
148