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
2
교육일정
1 일차 COM 프로그래밍 개요
2 일차 Visual C++ COM 컴파일러
3 일차 ATL 개요
4 일차 자동화와 이중 인터페이스
5 일차 이벤트 , 재사용 , 스레딩 모델
3
COM 프로그래밍 개요
4
Component Object Model 자신의 고유한 기능을 제공하는 단위 어플리케이션
( 즉 , 컴포넌트 ) 의 통합 및 커뮤니케이션 방법에 대한 표준을 정의한 사양 COM 컴포넌트를 정의하는 방법에 대한 표준이면서
COM 컴포넌트를 사용하는 방법에 대한 표준임 핵심적임 MS 의 기반 기술 역할 제공
OLE, ActiveX, ADO, OLE DB, Active Directory 모두 COM 을 기반으로 작성
5
COM 의 목표 각 컴포넌트는 서로 다른 언어로 개발될 수 있어야
한다 . 컴포넌트가 설치되고 실행하는 위치에 관계없이
같은 방법으로 컴포넌트를 사용할 수 있어야 한다 . 컴포넌트의 버전 관리가 쉬워야 한다 . 한 업체에 종속적이지 않아야 한다 .
6
COM 컴포넌트의 조건 언어 독립적이어야 한다 . 이진 형태로 제공되어야 한다 . 버전 호환성을 제공해야 한다 . 위치 투명성을 제공해야 한다 .
7
COM 인터페이스 (1) COM 객체가 자신의 기능을 노출시키는 기본적인
방법 COM 객체와 이를 사용하는 클라이언트 사이의
계약
COM컴포넌트COM
컴포넌트COM
클라이언트COM
클라이언트COM
인터페이스
8
COM 인터페이스 (2) 인터페이스의 의미
논리적 의미 특정 서비스를 제공하는 일련의 함수 ( 메서드 ) 들 ~ 하는 기능 ( 서비스 )
물리적 의미 함수 포인터 배열 형태의 메모리 구조 C++ 의 가상함수테이블 순수 가상함수로만 구성되는
추상 클래스로 정의 각 COM 객체는 반드시 IUnknown
인터페이스와 COM 객체 고유의 기능을 노출하는 하나 이상의 인터페이스를 제공
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
10
COM 클라이언트 / 컴포넌트 COM 컴포넌트 : 자신의 고유한 서비스 제공
COM 서버 : 물리적인 파일 (DLL, EXE) 의 실행 인스턴스
인 - 프로세스 서버 (In-proc Server) : DLL 파일로 구현 아웃 - 오브 - 프로세스 서버 (Out-of-process Server) : EXE
파일로 구현 로컬 서버 (Local Server) 리모트 서버 (Remote Server)
COM 객체 : COM 인터페이스를 구현한 클래스의 인스턴스
COM 클라이언트 : COM 컴포넌트의 서비스를 사용
11
GUID Globally Unique Identifier 128 bit 크기의 정수 값 ( 구조체로 정의 ) 전세계적으로 시간과 장소에 관계없이 고유하다고
보장할 수 있는 값 UUID 에서 유래 GUID 의 사용
IID : 인터페이스 ID ( 인터페이스 식별자 ) CLSID : 클래스 ID (COM 객체 식별자 )
GUIDGEN.EXE 로 생성
12
HRESULT 대부분의 COM 인터페이스 함수는 HRESULT
를 리턴 32 bit 정수값 (LONG) SUCCEEDED, FAILED 매크로와 함께 사용
Return CodeFacility code31 30 16 15 0
Severity code
15 bit 16 bit
13
COM 컴포넌트 사용
14
COM 컴포넌트 등록 레지스트리에 반드시 등록 후 사용
서버 유형인 - 프로세스
서버아웃 - 오브 - 프로세스
서버
레지스트리 등록
Regsvr32 DLL 파일명 EXE 파일명 /RegServer
레지스트리 해제 Regsvr32 /u DLL 파일명 EXE 파일명 /UnregServer
15
COM 클라이언트 작성1. COM 라이브러리 초기화2. COM 객체의 CLSID 구함3. COM 객체의 인스턴스 생성4. COM 객체가 제공하는 인터페이스 포인터를
구하여 메서드 호출 (COM 객체의 서비스 사용 )
5. COM 라이브러리의 초기화를 해제
16
COM 라이브러리 초기화 / 해제 COM 라이브러리
COM 을 사용하는 모든 application 에서 유용하게 사용될 수 있는 컴포넌트 관리 서비스 제공
HRESULT CoInitialize(LPVOID); 프로세스마다 한번만 호출
HRESULT CoInitializeEx(LPVOID, DWORD); #define _WIN32_DCOM 매크로 정의 후 사용 두번째 인자로 COINIT_APARTMENTTHREADED
지정 void CoUninitialize();
CoInitialize 와 짝을 이루어 호출
17
COM 객체의 CLSID 구하기1. CLSID 가 정의된 소스 파일 이용2. 레지스트리 편집기나 OLE/COM
개체뷰어를 사용하여 CLSID 구하기3. ProgID 사용
읽기 쉬운 문자열 형태의 식별자 < 컴포넌트 명 >.< 객체 명 >.< 버전 > HKEY_CLASSES_ROOT 의 서브키로 등록 사용은 간편하지만 유일성 보장되지 않음
4. 형식 라이브러리 이용
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}
…
19
CLSID 와 ProgID 변환 HRESULT CLSIDFromProgID(LPCOLESTR, LPCL
SID); HRESULT ProgIDFromCLSID(REFCLSID, LPOLE
STR);
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 로 처리
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 객체가 리턴하는 인터페이스 포인터의
주소
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;} ;
23
QueryInterface 클라이언트가 COM 객체의 다른 인터페이스를
요청할 때 해당 인터페이스 포인터를 리턴 COM 객체가 제공하는 인터페이스를 구하는
유일한 방법임
24
AddRef/Release COM 객체가 스스로 자신이 몇 번 참조되어
있는가 하는 횟수를 관리 레퍼런스 카운터가 0 일 때 스스로를 소멸 참조 카운터 규칙
1. 인터페이스를 리턴하기 전에 AddRef 한다 .2. 인터페이스의 사용이 끝나면 Release 한다 .3. 인터페이스 포인터를 다른 인터페이스 포인터에
대입할 때도 AddRef 한다 .
25
COM 객체의 사용 인터페이스 포인터를 통하여 인터페이스가
제공하는 메서드 호출 QueryInterface 를 통하여 COM 객체가
지원하는 다른 인터페이스를 요청 인터페이스의 사용이 끝나면 Release 호출 COM 서버가 할당한 메모리를 클라이언트가
해제해야 하는 경우 CoTaskMemAlloc, CoTaskMemFree 사용
26
COM 컴포넌트 구현
27
COM 컴포넌트의 구현1. COM 인터페이스의 정의2. COM 객체 클래스 구현3. 클래스 팩토리 클래스 구현4. COM 서버 구현
28
COM 인터페이스 정의 IDL 로 COM 인터페이스 정의
MIDL 컴파일러로 컴파일 IDL(Interface Definition Language)
인터페이스를 정의하는 표준 개발 도구 MIDL 컴파일러 제공 OSF RPC 의 IDL 을 확장 C/C++ like language with attribute 언어 독립성 제공 ( 형식 라이브러리 ) 위치투명성 제공 ( 프록시 / 스텁 코드 )
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 종료 문자열
30
MIDL 컴파일러의 역할 C/C++ 에서 사용할 수 있는 인터페이스를
정의한 코드를 포함하는 헤더 파일 생성 커스텀 인터페이스에 대한 프록시 (proxy)/
스텁 (stub) 코드 생성 자동화에서 사용되는 형식 라이브러리 (Type L
ibrary) 생성 library, coclass 문이 사용되는 경우에만 생성
31
COM 객체 구현 인터페이스 포함
COM 객체 클래스 안에 인터페이스 구현 클래스를 포함
구문은 복잡하지만 디버깅이 쉽다 . MFC 에서 사용
인터페이스 상속 COM 객체 클래스를 인터페이스에서 상속 간단하다 . ATL 에서 사용
32
COM 객체 클래스 정의
class CHello : public IHello, public IGoodbye{
// IUnknown 메서드 구현// IHello 메서드 구현// IGoodbye 메서드 구현
};
class CHello : public IHello, public IGoodbye{
// IUnknown 메서드 구현// IHello 메서드 구현// IGoodbye 메서드 구현
};
다중 인터페이스 구현을 위해 다중 상속 이용 COM 객체 구현 클래스에서는 모든
인터페이스 메서드를 재정의해야 한다 .
33
다중 상속 시 메모리 구조
CHello::thisQueryInterfaceQueryInterfaceIHello vptrIHello vptr
AddRefAddRefReleaseReleasesayHellosayHello
QueryInterfaceQueryInterfaceAddRefAddRef
ReleaseReleasesayGoodbyesayGoodbye
IGoodbye vptrIGoodbye vptr
멤버 변수멤버 변수
IHello
IGoodbye
(IHello*)CHello::this
(IGoodbye*)CHello ::this
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; }
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 하게 증가 , 감소
36
COM 객체 서비스 메서드 구현 COM 객체 고유한 서비스를 제공하는
인터페이스의 메소드를 구현 필요 시 생성자 , 소멸자 , 멤버 변수 추가 가능 COM 객체의 CLSID 정의
GUIDGEN 이용
37
클래스 팩토리의 구현 COM 컴포넌트는 COM 객체의 인스턴스를
생성할 수 있는 매커니즘 제공해야 함 클래스 팩토리가 COM 객체의 인스턴스를
생성 클래스 팩토리도 일종의 COM 객체 IClassFactory 를 반드시 제공해야 함
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;};
39
컴포넌트의 생성 과정Client Server
CoGetClassObject 호출
COM LibraryCoGetClassObjectCoGetClassObject DllGetClassObjectDllGetClassObject
CFactoryCFactory
클래스팩토리생성
컴포넌트생성
pIClassFactory
IClassFactory 리턴
IClassFactory::CreateInstance 호출
pIHellopIHello::sayHello 호출
IHello 리턴
pIClassFactory->Release() 호출CHelloCHello
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;}
41
인 - 프로세스 서버의 구현 클라이언트에서 COM 객체 사용 시 호출되는
4 개의 익스포트 함수 구현 DllGetClassObject
CoGetClassObject 에 의해 호출 DllRegisterServer, DllUnregisterServer
REGSVR32.EXE 에 의해 호출 DllCanUnloadNow
CoFreeUnusedLibrary 에 의해 호출
42
로컬 서버 구현 Win32 에서는 프로세스마다 주소공간이
다르므로 로컬 서버나 리모트 서버가 리턴한 인터페이스 포인터는 클라이언트 주소공간에서는 의미가 없다 .
위치 투명성을 제공하기 위해서는 프록시와 스텁이 필요
43
프록시 (Proxy)/ 스텁 (Stub)
ClientEXE
ClientEXE
ProxyDLL
ProxyDLL
ServerEXE
ServerEXE
StubDLL
StubDLL
Process BoundaryNetwork Boundary
LPCRPC
마샬링 언마샬링
44
마샬링1. 표준 인터페이스의 마샬링
운영체제에 의해 제공 (ole32.dll)
2. 자동화 인터페이스의 마샬링 운영체제에 의해 제공 (oleaut32.dll)
3. 커스텀 인터페이스의 마샬링 MIDL 컴파일 결과로 만들어지는 코드 이용 IDL 작성 시 속성 지정
45
로컬 서버의 구현 인 - 프로세스 서버와는 달리 CoInitialize, CoU
ninitialize 호출 DllRegisterServer, DllUnregisterServer
명령행 인자 처리로 구현 /RegServer, /UnregServer 옵션 처리
DllGetClassObject CoRegisterClassObject, CoRevokeClassObject
이용 DllCanUnloadNow
스스로 능동적으로 종료
46
CoRegisterClassObject (1) COM 은 내부적으로 등록된 클래스 팩토리 CO
M 객체를 저장하는 ROT 관리 클라이언트가 CoGetClassObject 호출 시 RO
T 부터 검사함 등록이 안된 경우 /Embedding 옵션으로 로컬
서버 실행 클래스 팩토리 COM 객체를 ROT 에 등록함
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 에서 사용 )
48
로컬 서버 종료 다음 조건 만족 시 능동적으로 종료
COM 객체 카운터가 0 이고 클라이언트가 IClassFactory::LockServer(FALSE) 를 호출함으로써 마지막 로크 카운터가 0 이 될 때
로크 카운터가 현재 0 이고 , 클라이언트가 IUnknown::Release 를 호출하여 마지막 COM 객체 카운터가 0 이 될 때
PostQuitMessage 함수 이용
49
VC++ COM 컴파일러
50
VC++ 의 COM 지원 #import __declspec 확장 속성 : uuid, property __uuidof _com_ptr_t 클래스 _com_error 클래스 _bstr_t 클래스 _variant_t 클래스
51
형식 라이브러리 COM 컴포넌트가 노출하는 COM 객체에 대한
정보를 포함하는 일종의 복합 도큐먼트 (*.tlb) COM 객체에 대한 매뉴얼 IDL 의 library 문과 coclass 문에 의해 제공 DLL 이나 EXE 파일에 리소스로 포함 가능 (
리소스 스크립트에 선언 ) 형식 라이브러리 자체도 ITypeLib, ITypeLib2, ITy
peInfo, ITypeInfo2, ITypeComp 인터페이스를 노출하는 하나의 COM 객체임
HKEY_CLASSES_ROOT\TypeLib 서브키에 저장
52
#import #import “파일이름” [속성 리스트 ] #import “progid:XXX.XXX.XXX” [속성 리스트 ] #import “libid:XXXXXX” [속성 리스트 ]
속성 리스트 : no_namespace, named_guids 2 개의 C++ 소스 코드 생성
*.tlh : 스마트 포인터 클래스 , 인터페이스의 정의 포함
*.tli : COM 객체의 메서드를 호출하는 구현 코드 포함 ( 래퍼 함수 )
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;
54
스마트 포인터 인터페이스 포인터를 관리해주는 클래스 operator-> 함수를 제공
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;
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();
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());}
58
_com_error 클래스 (2) 멤버함수
Error : HRESULT 값 리턴 ErrorInfo : IErrorInfo 인터페이스 리턴 Description, HelpContext, HelpFile, Source, GUID :
IErrorInfo 메서드 제공 ErrorMessage : HRESULT 에 대한 문자열 형태의
에러 메시지 리턴
59
_bstr_t, _variant_t 클래스 _bstr_t
BSTR 를 캡슐화 SysAllocString과 SysFreeString 호출
_variant_t VARIANT 를 캡슐화 VariantInit과 VariantClear 호출
60
ATL 프로그래밍 기초
61
Active Template Library ATL 의 목적
작고 빠르고 확장성을 갖는 COM 객체를 손쉽게 구현 (IUnknown, IClassFactory 의 구현 제공 )
MFC vs ATL MFC
풍부한 사용자 인터페이스 기능 제공 MFC 디폴트 프레임워크 크기 , 속도 문제
ATL 프레임워크 불필요 . 실행 라이브러리 불필요 작고 빠른 COM 컴포넌트 생성에 적합
업무 로직을 구현하는 자동화 컴포넌트 구현에 적합
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);
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;
64
ATL 프로젝트 마법사 (1) 서버 유형에 따라 ATL 프로젝트에 필요한 골격
코드와 프로젝트 파일 생성 서버 유형
동적 연결 라이브러리 (DLL) 인 - 프로세스 서버 COM 컴포넌트 생성 DllMain, DllGetClassObject, DllCanUnloadNow, DllRegisterServe
r, DllUnregisterServer 의 구현 코드 제공 실행 파일 (EXE)
아웃 - 오브 - 프로세스 서버 COM 컴포넌트 생성 WinMain안에 레지스트리 등록 및 해제 , ROT 에 클래스 팩토리
등록 및 해제 , 메시지 루프 등 기본 코드 제공 서비스 (Service)
Windows NT 서비스 생성
65
ATL 프로젝트 마법사 (2) 추가 옵션
프록시 / 스텁 코드 병합 허용 COM 컴포넌트와 같은 DLL안에 프록시 / 스텁 코드 추가
MFC 지원 COM+ 1.0 지원
특성 사용 (ATL 7.0) attribute 기반 프로그래밍 지원 기능 추가 좀더 쉽게 COM 프로그래밍을 할 수 있도록 attrib
ute 제공 필요한 코드 생성
66
서버 모듈 클래스 (ATL 3.0) CComModule 클래스
COM 서버 모듈 구현 클라이언트에서 COM 서버내의 COM 객체로 접근하는 기능 제공
인 - 프로세스 서버와 아웃 - 오브 - 프로세스 서버를 모두 지원
객체 맵 (Object Map) 을 사용하여 COM 서버 모듈에 포함된 COM 객체 관리
// COM 서버 모듈 전역 객체 정의CComModule _Module; // COM 서버 모듈 전역 객체 정의CComModule _Module;
67
서버 모듈 클래스 (ATL 3.0) CComModule 멤버
Init : 멤버 변수 초기화 Term : 멤버 변수 해제 GetClassObject : 클래스 팩토리 생성 (DLL 전용 ) GetLockCount : 로크 카운터 리턴 RegisterServer : 레지스트리에 객체 맵에 정의된 CO
M 객체 등록 UnregisterServer : 레지스트리에서 객체 맵에
정의된 COM 객체의 등록 해제 RegisterClassObjects : ROT 에 클래스 팩토리 등록
(EXE 전용 ) RevokeClassObjects : ROT 에서 클래스 팩토리의
등록 해제 (EXE 전용 )
68
서버 모듈 클래스 (ATL 7.0) 인 - 프로세스 서버와 아웃 - 오브 - 프로세스
서버의 기능을 나눠서 제공할 수 있도록 계층 구조 정의
CAtlDllModuleTCAtlDllModuleT CAtlExeModuleTCAtlExeModuleT
CAtlModuleTCAtlModuleT
CAtlModuleCAtlModule
ATL_MODULEATL_MODULE
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()
70
객체 맵 (Object Map) (ATL 7.0) ATL 7.0 에서는 별도의 객체 맵을 따로
관리하는 대신 OBJECT_ENTRY_AUTO 매크로 사용 OBJECT_ENTRY_AUTO 매크로는 COM 서버 내에
COM 객체를 추가할 때마다 COM 객체의 구현 클래스 헤더 파일에 함께 생성됨
링크 단계에서 객체 맵을 링커가 하나로 합쳐줌
71
ATL 단순 개체 마법사 COM 서버에 COM 객체 추가 COM 객체의 카테고리와 유형 선택 이름 페이지
약식 이름 : COM 객체명 입력 속성 페이지
스레딩 모델 인터페이스 집합체 지원
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{…};
73
ATL COM 객체의 계층 구조
CComObjectRootExCComObjectRootEx CComCoClassCComCoClass
CHelloCHello
CCom(Agg)ObjectCCom(Agg)Object
COM 객체의구현클래스
추상클래스
COM 객체의인스턴스 생성시사용되는 클래스
74
CComObjectRootEx IUnknown 인터페이스의 내부 구현 제공 스레딩 모델에 따른 레퍼런스 카운트 구현
CComSingleThreadingModel CComMultiThreadingModel
멤버 함수 InternalQueryInterface, InternalAddRef, InternalRele
ase : IUnknown 의 내부 구현 OuterQueryInterface, OuterAddRef, OuterRelease :
통합시 외부 COM 객체로 IUnknown 을 위임하는 코드 구현
FinalConstruct, FinalRelease : COM 객체의 초기화 , 해제를 위한 가상 함수
75
CComCoClass (1) DECLARE_CLASSFACTORY() 매크로
COM 객체의 디폴트 클래스 팩토리 정의 CComClassFactory
IClassFactory 의 구현 제공 CComCreator
클래스 팩토리 COM 객체의 인스턴스 생성
DECLARE_CLASSFACTORY() 확장후typedef CComCreator< CComObject< CComClassFactory > > _ClassFactoryCreatorClass;
DECLARE_CLASSFACTORY() 확장후typedef CComCreator< CComObject< CComClassFactory > > _ClassFactoryCreatorClass;
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;
77
CCom(Agg)Object 사용자 COM 객체 클래스를 템플릿의 인자로 받아 사용자 COM 객체 클래스를 상속받는 클래스로 정의됨
QueryInterface, AddRef, Release 의 구현 제공 실제 COM 객체의 인스턴스 생성 시 이용되는
클래스임
template <class Base>class CComObject : public Base{…};
template <class Base>class CComObject : public Base{…};
78
ATL_NO_VTABLE __declspec(novtable) 로 확장 클래스의 생성자와 소멸자에서 가상함수
테이블과 vptr 가 초기화되는 것을 막아줌 사용자 COM 객체 클래스는 추상클래스
이므로 가상함수 테이블이나 vptr 을 초기화할 필요가 없음 파생클래스 (CComObject) 에서 처리됨
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()
80
Property, Method 추가 속성 (Property) 추가 마법사
클래스뷰에서 ‘속성 추가’ 선택 속성 형식 , 속성 이름 지정 함수 형식
Get 함수 : 읽기 함수 Put 함수 : 쓰기 함수
COM 객체 구현 클래스에 멤버 변수 추가 ( 초기화 )
메서드 (Method) 추가 마법사 클래스뷰에서 ‘메서드 추가’ 선택
81
새로운 인터페이스의 추가 직접 수정하는 경우
IDL 파일에 인터페이스 정의 COM 객체 클래스가 인터페이스를 상속받도록
수정 COM 맵에 인터페이스 추가 인터페이스의 메서드 구현
인터페이스 구현 마법사를 사용하는 경우 IDL 파일에 인터페이스 정의 MIDL 컴파일 클래스뷰에서 ‘인터페이스 구현’ 선택
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); // 에러
83
CComBSTR, CComVariant CComBSTR
주요멤버 m_str : BSTR 값 Append, AppendBSTR, Copy, Empty, Length operator BSTR, =, +=, &, !
CComVariant 주요 멤버
ChangeType, Clear, Copy operator =, ==, !=
84
ATL Debugging _ATL_DEBUG_INTERFACES
<atlbase.h>앞에 정의 Reference Counting과 QueryInterface 디버깅
_ATL_DEBUG_QI <atlcom.h>앞에 정의 QueryInterface 디버깅
ATLTRACE(…) Output 윈도우에 디버깅 정보 출력
_ATL_MIN_CRT CRT 라이브러리 제거 (Release 모드 )
85
자동화와 이중 인터페이스
86
자동화 (Automation) 어플리케이션이 자신의 기능을 다른
어플리케이션에 노출하는 기능을 자동화라고 함 자동화 컴포넌트 , 자동화 서버
자동화 서비스를 제공하는 어플리케이션 자동화 컨트롤러 , 자동화 클라이언트
자동화 서비스를 사용하는 어플리케이션 자동화 객체
자동화 서비스를 제공하는 객체 메서드 (Method), 속성 (Property), 이벤트
(Event) 를 통해 기능 제공
87
자동화의 특징 스크립트 언어에서는 COM 객체를 사용할 수
없음 가상함수 테이블을 인식할 수 없음 자료형 개념이 불분명함
이를 해결하기 위한 방법이 자동화 기술 가상함수 테이블 인식 문제 IDispatch 로 해결 자료형 문제 자동화 데이터 타입으로 해결
88
자동화의 장단점 장점
스크립트 클라이언트에서도 자동화 객체 사용 가능 마샬링 코드를 운영체제가 제공 (oleaut32.dll)
단점 실행 속도가 저하됨 사용할 수 있는 데이터형이 제한됨
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);
};
90
자동화의 구현 디스패치 인터페이스 (dispinterface)
Invoke 함수는 DISPID 를 함수 포인터 배열의 인덱스로 사용하여 함수 호출
COM 인터페이스가 아님 불변성 X 메서드가 HRESULT 를 리턴할 필요 없음
이중 인터페이스 (dual interface) dispinterface 의 속도 저하 문제 개선 IDispatch 를 상속받는 COM interface 를 사용
속도 개선 효과
91
후기 바인딩 vs 초기 바인딩 후기 바인딩
실행 시간에 DISPID 결정 속도가 느리며 잘못된 속성명이나 메서드명 사용
시 실행 에러 발생 컴파일 시 데이터 형 검사 X 실행 에러 발생 실행 시간에 자동화 객체의 기능 결정
초기 바인딩 ID 바인딩 형식 라이브러리에서 DISPID 정보를 읽어서 사용
후기 바인딩에 비해 속도가 빠름 컴파일 시 데이터 형 검사 가능
92
VARIANT 데이터형 서로 다른 데이터형을 멤버로 갖는 16 byte
크기의 공용체 (union) VARIANT 관련 함수
VariantInit : VARIANT 구조체 초기화 VariantChangeType : 데이터형 변환
93
BSTR, SAFEARRAY BSTR
byte-length prefixed unicode string SysAllocString, SysFreeString 이용
SAFEARRAY 배열 범위에 대한 정보를 포함하고 있는 배열 SafeArray… 함수 이용
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()};
95
MFC 자동화 클라이언트 ‘클래스 추가’에서 ‘ TypeLib 의 MFC 클래스’
로 자동화 객체에 대한 wrapper class 생성 CAddBack COleDispatchDriver 의 파생 클래스 IDispatch 를 통한 메서드 , 속성 호출을 처리하는 멤버 함수 제공
자동화 객체 생성시 COleDispatchDriver::CreateDispatch 이용 ProgID 나 CLSID 로 자동화 객체 생성
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; };};
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()};
98
DISPID 인코딩 multiple dual interface 를 갖는 자동화 객체의
모든 기능을 제공하기 위한 방법 COM 객체 구현 클래스에 GetIDsOfNames, In
voke 를 재정의 GetIDsOfNames : DISPID 를 찾아 인터페이스
정보를 함께 인코딩하여 리턴 Invoke : 넘겨받은 DISPID 를 디코딩하여 어느
인터페이스의 함수를 호출할 지 결정
DISPID
인코딩된 DISPID (4 byte)인터페이스 구분
2 byte 2 byte
99
에러 정보
자동화객체
ISupportErrorInfo
에러 정보객체
ICreateErrorInfo
IErrorInfo
CreateErrorInfo( 에러정보객체 생성 )
ICreateErrorInfo 로에러 정보 지정
에러정보객체의 IErrorInfo 구함
SetErrorInfo( 현재 스레드와 에러정보객체연결 )
자동화클라이언트
ISupportErrorInfo구함
GetErrorInfo 로 IErrorInfo 구함
InterfaceSupportsErrorInfo 호출하여 에러 정보 제공 여부 확인
IErrorInfo 로 에러 정보 추출
100
에러 정보 리턴 (1) 자동화 객체에서는 IErrorInfo 객체를
생성하여 여기에 발생한 에러에 대한 자세한 정보를 제공
자동화 객체는 ISupportErrorInfo 인터페이스를 지원해야 함 ATL 단순개체마법사의 옵션 페이지의 ‘ ISupportErr
orInfo’ 지원 선택 InterfaceSupportsErrorInfo : 해당 인터페이스가
자세한 에러 정보를 제공하는 지 여부를 결정
101
에러 정보 리턴 (2) 에러 정보 제공
CreateErrorInfo 함수를 호출하여 에러 정보 객체 생성
ICreateErrorInfo 인터페이스로 에러 정보 저장 SetSource : 에러가 발생한 자동화 객체명 지정 (ProgID) SetDescription : 에러에 대한 설명 텍스트 지정 SetGUID : 에러를 정의한 인터페이스의 GUID 지정 SetHelpFile : 에러를 설명하는 도움말파일의 경로명 지정 SetHelpContext : 에러에 대한 도움말 컨텍스트 ID 지정
SetErrorInfo 함수를 호출하여 에러 정보 객체를 현재의 스레드와 연결
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;}
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;}
104
에러 정보 받기 자동화 객체에서 에러 정보를 받는 방법
IDispatch::Invoke 의 EXCEPINFO 인자 사용 직접 에러 정보 객체를 사용
에러 발생시 ISupportErrorInfo 인터페이스를 구하여 InterfaceSupportsErrorInfo 함수를 호출 IErrorInfo 를 지원하는지 여부 확인
GetErrorInfo 를 호출하여 IErrorInfo 인터페이스를 구한 후 IErrorInfo 인터페이스의 메서드를 호출하여 에러 정보를 추출
GetSource, GetDescription, GetGUID, GetHelpFile, GetHelpContext 등 이용
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()};
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()};
107
이벤트 구현
108
이벤트 자동화 객체가 자신에게 어떠한 사건이 발생했다는
사실을 자동화 컨트롤러에게 알려주는 기능 커넥션 포인트 메커니즘으로 구현
자동화컨트롤러자동화
컨트롤러자동화
객체자동화
객체
Property/Method
Event
109
커넥션 포인트 (1) 소스 객체 (source object)
이벤트를 발생 (fire) 시키는 소스 인터페이스 (source interface, outgoing interface) 를 제공
자동화 객체가 소스 객체가 됨 싱크 오브젝트
이벤트를 받아들이는 싱크 인터페이스 (sink interface, incoming interface) 를 제공
자동화 컨트롤러 내에 별도로 생성 이벤트
자동화 객체가 호출하고 싱크 객체가 구현하는 싱크 인터페이스의 메서드
싱크 인터페이스와 소스 인터페이스는 서로 일치
110
커넥션 포인트 (2) 자동화 컨트롤러는 싱크 객체 생성 자동화 컨트롤러는 자동화 객체에게 IConnectionPoin
tContainer 요청 IConnectionPointContainer::FindConnectionPoint 호출
소스 인터페이스에 대한 IConnectionPoint 구함 IConnectionPoint::Advise 를 호출하여 싱크 객체의
인터페이스를 넘겨줌 자동화 객체와 자동화 컨트롤러 연결 자동화
객체는 필요할 때 이벤트를 발생 (fire) 시킴 IConnectionPoint::Unadvise 를 호출하여 커넥션
포인트를 단절
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{};
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;};
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>{};
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>{…};
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()
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;}
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();}
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 파일에 정의된 소스 인터페이스 메서드 구현
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("이벤트를 받을 수 없습니다 ."); …
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;}
121
dispinterface 싱크 객체 구현 (1) 싱크 인터페이스
이중 인터페이스 또는 dispinterface 로 구현 이중 인터페이스를 구현하는 싱크 객체의 경우
모든 이벤트를 구현해야 함 dispinterface 를 구현하는 싱크 객체의 경우 모든
이벤트를 구현할 필요가 없음 이벤트 싱크 구현을 위한 IDispEventImpl<>
클래스 사용
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()};
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("이벤트를 받을 수 없습니다 ."); …
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;}
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()};
126
COM 객체의 재사용
127
COM 의 재사용성 COM 은 구현 상속은 제공하지 않음 인터페이스
상속만을 제공 이진 레벨의 재사용 기법
포함 (Containment) 통합 (Aggregation)
OuterOuter
InnerInner
128
포함 (1) C++ 의 포함과 유사한 개념 외부 COM 객체는 내부 COM 객체를 이용하여
자신의 인터페이스를 구현 외부 COM 객체는 내부 COM 객체의 인터페이스
함수를 단순히 호출 Forwarding 가능 외부 COM 객체는 내부 COM 객체의 인터페이스
함수 호출에 다른 기능을 추가 Adding 가능 외부 COM 객체가 내부 COM 객체의 생성과
소멸을 모두 직접 관리 모든 COM 객체가 포함 가능
129
포함 (2)
CAddBackCAddBackIAddBack
CAddEndCAddEndIAddEnd
m_pAddEnd CoCreateInstance( CLSID_AddEnd,…);
CLSID_AddEnd
CLSID_AddBack
IAddEnd
130
통합 (1) 포함의 특별한 경우로 볼 수 있음 외부 COM 객체가 내부 COM 객체의
인터페이스를 직접 클라이언트에게 제공 외부 COM 객체는 인터페이스를 구현하지 않음 Forwarding O, Adding X
외부 COM 객체는 내부 COM 객체 생성 시 자신의 IUnknown 을 제공
외부 COM 객체는 내부 COM 객체 접근 시 내부 COM 객체의 IUnknown으로만 접근
통합을 지원하는 COM 객체만 통합 가능
131
통합 (2)
CAddBackCAddBackIAddBack
CAddEndCAddEnd
m_pUnk
CoCreateInstance( CLSID_AddEnd, (IUnknown*)this, CLSCTX_ALL, IID_IUnknown, (void**)&m_pUnk);
CLSID_AddEnd
CLSID_AddBack
IAddEnd
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;}
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 객체로 인터페이스 요청을 위임
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;}
135
COM 스레딩 모델
136
아파트먼트 (1) COM 객체의 인스턴스와 스레드를 포함하는
논리적인 영역 아파트먼트의 기능
스레드 동시성 (Thread Concurrency) 에 대한 논리적인 구조 제공
COM 환경에서 스레드 동시성을 얻기 위해 개발자가 따라야 할 프로그래밍 규칙 제공
개발자가 COM 객체의 스레드 동시성을 관리할 수 있게 하는 시스템 코드 제공
137
아파트먼트 (2) 모든 COM 객체는 하나의 아파트먼트에 속함 스레드는 하나의 아파트먼트에서만 실행
실행전에 아파트먼트에 들어가야 함 COM 객체는 해당 COM 객체의
아파트먼트에서 실행되는 스레드에 의해서만 접근 가능
아파트먼트의 유형 STA(Single-Threaded Apartment) MTA(Multi-Threaded Apartment)
138
STA (1) 하나의 스레드만 실행될 수 있는 아파트먼트 STA안의 COM 객체는 동시에 여러 스레드에
의해 접근 불가 스레드 동기화를 구현할 필요 없음 STA COM 객체의 메서드 호출은 COM 에 의해 동기화
139
STA (2)
STAProcess T1
COMobject
T1
T2
STA
COMobject
T2
허용되지 않음 (Marshaling 필요 )
140
MTA (1) 여러 스레드가 동시에 실행될 수 있는
아파트먼트 MTA안의 COM 객체는 동시에 여러 스레드에
의해 접근 가능 MTA COM 객체는 동기화 객체를 이용하여
스레드 동기화를 구현해야 함
141
MTA (2)
MTAProcess T1 T1
T2 T2
COMobject
동시에 접근 ( 동기화 필요 )
142
아파트먼트 생성 HRESULT CoInitializeEx(void *pvReserved, DW
ORD dwFlags); dwFlags
COINIT_APARTMENTTHREADED : STA 생성 COINIT_MULTITHREADED : MTA 생성
HRESULT CoInitialize(void *pvReserved); HRESULT OleInitialize(void *pvReserved);
143
인 - 프로세스 서버의스레딩 모델 표시 아웃 - 오브 - 프로세스 서버는 CoInitializeEx 를
호출하여 명확하게 어떤 아파트먼트를 사용할 지 결정
인 - 프로세스 서버의 경우 COM 객체의 CLSID 레지스트리 서브키 밑에 InprocServer32 서브키의 ThreadingModel 에 지원하는 아파트먼트 유형을 지정 키가 없는 경우 : 단일 스레딩 모델 Apartment : 아파트 스레딩 모델 Free : 자유형 스레딩 모델 Both : 모두 스레딩 모델
144
스레딩 모델 단일 스레딩 모델 (Single Threading Model)
주 STA 에만 직접 생성 가능함 아파트 스레딩 모델 (Apartment Threading Mod
el) STA 에만 직접 생성 가능함
모두 스레딩 모델 (Mixed Threading Model) STA, MTA 에 모두 직접 생성 가능함
자유형 스레딩 모델 (Free Threading Model) MTA 에만 직접 생성 가능함
145
인터페이스 마샬링 COM 은 마샬링을 사용하여 서로 다른
아파트먼트 영역 사이에 인터페이스 포인터를 전달
마샬링 / 언마샬링은 COM 에 의해 자동적으로 수행
아파트먼트 사이에 명확히 인터페이스 포인터를 마샬링 / 언마샬링 하는 경우 HRESULT CoMarshalInterThreadInterfaceInStream
(REFIID riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);
HRESULT CoGetInterfaceAndReleaseStream(LPSTREAM pStm, REFIID riid, LPVOID* ppv);
146
ATL/COM 관련 레퍼런스
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
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