1 / 148

Visual C++ ATL/COM Programming

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

filia
Télécharger la présentation

Visual C++ ATL/COM Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Visual C++ ATL/COM Programming 천 정 아 jachun3@kornet.net

  2. 교육일정

  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 인터페이스

  8. COM 인터페이스 (2) • 인터페이스의 의미 • 논리적 의미 • 특정 서비스를 제공하는 일련의 함수(메서드)들 • ~하는 기능(서비스) • 물리적 의미 • 함수 포인터 배열 형태의 메모리 구조 • C++의 가상함수테이블  순수 가상함수로만 구성되는 추상 클래스로 정의 • 각 COM 객체는 반드시 IUnknown 인터페이스와 COM 객체 고유의 기능을 노출하는 하나 이상의 인터페이스를 제공

  9. vptr 멤버 변수 인터페이스 메모리 구조 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 구현 }; 가상함수테이블 인스턴스 인터페이스 포인터 QueryInterface AddRef Release Method1 Method2 Method3

  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 매크로와 함께 사용 15 bit 16 bit Facility code Return Code 31 30 16 15 0 Severity code

  13. COM 컴포넌트 사용

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

  15. COM 클라이언트 작성 • COM 라이브러리 초기화 • COM 객체의 CLSID 구함 • COM 객체의 인스턴스 생성 • COM 객체가 제공하는 인터페이스 포인터를 구하여 메서드 호출 (COM 객체의 서비스 사용) • COM 라이브러리의 초기화를 해제

  16. COM 라이브러리 초기화/해제 • COM 라이브러리 • COM을 사용하는 모든 application에서 유용하게 사용될 수 있는 컴포넌트 관리 서비스 제공 • HRESULT CoInitialize(LPVOID); • 프로세스마다 한번만 호출 • HRESULT CoInitializeEx(LPVOID, DWORD); • #define _WIN32_DCOM 매크로 정의 후 사용 • 두번째 인자로 COINIT_APARTMENTTHREADED 지정 • void CoUninitialize(); • CoInitialize와 짝을 이루어 호출

  17. COM 객체의 CLSID 구하기 • CLSID가 정의된 소스 파일 이용 • 레지스트리 편집기나 OLE/COM 개체뷰어를 사용하여 CLSID 구하기 • ProgID 사용 • 읽기 쉬운 문자열 형태의 식별자 • <컴포넌트 명>.<객체 명>.<버전> • HKEY_CLASSES_ROOT의 서브키로 등록 • 사용은 간편하지만 유일성 보장되지 않음 • 형식 라이브러리 이용

  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  ProgID CLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95} MediaPlayer.MediaPlayer  VerIndProgID CurVer : MediaPlayer.MediaPlayer.1 CLSID : {22D6F312-B0F6-11D0-94AB-0080C74C7E95}

  19. CLSID와 ProgID 변환 • HRESULT CLSIDFromProgID(LPCOLESTR, LPCLSID); • HRESULT ProgIDFromCLSID(REFCLSID, LPOLESTR);

  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, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID iid, LPVOID* ppv); • rclsid : COM 객체의 CLSID • pUnkOuter : 통합에서만 사용 • dwClsContext : 실행 형태 (CLSCTX_INPROC_SERVER, 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; } ;

  23. QueryInterface • 클라이언트가 COM 객체의 다른 인터페이스를 요청할 때 해당 인터페이스 포인터를 리턴 • COM 객체가 제공하는 인터페이스를 구하는 유일한 방법임

  24. AddRef/Release • COM 객체가 스스로 자신이 몇 번 참조되어 있는가 하는 횟수를 관리 • 레퍼런스 카운터가 0일 때 스스로를 소멸 • 참조 카운터 규칙 • 인터페이스를 리턴하기 전에 AddRef 한다. • 인터페이스의 사용이 끝나면 Release 한다. • 인터페이스 포인터를 다른 인터페이스 포인터에 대입할 때도 AddRef 한다.

  25. COM 객체의 사용 • 인터페이스 포인터를 통하여 인터페이스가 제공하는 메서드 호출 • QueryInterface를 통하여 COM 객체가 지원하는 다른 인터페이스를 요청 • 인터페이스의 사용이 끝나면 Release 호출 • COM 서버가 할당한 메모리를 클라이언트가 해제해야 하는 경우 CoTaskMemAlloc, CoTaskMemFree 사용

  26. COM 컴포넌트 구현

  27. COM 컴포넌트의 구현 • COM 인터페이스의 정의 • COM 객체 클래스 구현 • 클래스 팩토리 클래스 구현 • 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); }; • 메서드의 인자 헤더 • in : 클라이언트에서 서버로 이동(마샬링) • out : 서버에서 클라이언트로 이동(마샬링) • string : NULL 종료 문자열

  30. MIDL 컴파일러의 역할 • C/C++에서 사용할 수 있는 인터페이스를 정의한 코드를 포함하는 헤더 파일 생성 • 커스텀 인터페이스에 대한 프록시(proxy)/스텁(stub) 코드 생성 • 자동화에서 사용되는 형식 라이브러리(Type Library) 생성 • library, coclass 문이 사용되는 경우에만 생성

  31. COM 객체 구현 • 인터페이스 포함 • COM 객체 클래스 안에 인터페이스 구현 클래스를 포함 • 구문은 복잡하지만 디버깅이 쉽다. • MFC에서 사용 • 인터페이스 상속 • COM 객체 클래스를 인터페이스에서 상속 • 간단하다. • ATL에서 사용

  32. COM 객체 클래스 정의 • 다중 인터페이스 구현을 위해 다중 상속 이용 • COM 객체 구현 클래스에서는 모든 인터페이스 메서드를 재정의해야 한다. class CHello : public IHello, public IGoodbye { // IUnknown 메서드 구현 // IHello 메서드 구현 // IGoodbye 메서드 구현 };

  33. 다중 상속 시 메모리 구조 CHello::this IHello vptr QueryInterface (IHello*)CHello::this IGoodbye vptr AddRef IHello 멤버 변수 Release (IGoodbye*)CHello ::this sayHello QueryInterface AddRef IGoodbye Release sayGoodbye

  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; }

  35. 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; }

  36. COM 객체 서비스 메서드 구현 • COM 객체 고유한 서비스를 제공하는 인터페이스의 메소드를 구현 • 필요 시 생성자, 소멸자, 멤버 변수 추가 가능 • COM 객체의 CLSID 정의 • GUIDGEN 이용

  37. 클래스 팩토리의 구현 • COM 컴포넌트는 COM 객체의 인스턴스를 생성할 수 있는 매커니즘 제공해야 함 • 클래스 팩토리가 COM 객체의 인스턴스를 생성 • 클래스 팩토리도 일종의 COM 객체 • IClassFactory를 반드시 제공해야 함

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

  39. Client COM Library Server 클래스 팩토리 생성 IClassFactory 리턴 IClassFactory::CreateInstance 호출 CFactory IHello 리턴 CHello pIHello::sayHello호출 컴포넌트 생성 컴포넌트의 생성 과정 CoGetClassObject 호출 CoGetClassObject DllGetClassObject pIClassFactory pIHello pIClassFactory->Release() 호출

  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; }

  41. 인-프로세스 서버의 구현 • 클라이언트에서 COM 객체 사용 시 호출되는 4개의 익스포트 함수 구현 • DllGetClassObject • CoGetClassObject에 의해 호출 • DllRegisterServer, DllUnregisterServer • REGSVR32.EXE에 의해 호출 • DllCanUnloadNow • CoFreeUnusedLibrary에 의해 호출

  42. 로컬 서버 구현 • Win32에서는 프로세스마다 주소공간이 다르므로 로컬 서버나 리모트 서버가 리턴한 인터페이스 포인터는 클라이언트 주소공간에서는 의미가 없다. • 위치 투명성을 제공하기 위해서는 프록시와 스텁이 필요

  43. 프록시(Proxy)/스텁(Stub) Process Boundary Network Boundary Client EXE Server EXE Proxy DLL Stub DLL LPC RPC 마샬링 언마샬링

  44. 마샬링 • 표준 인터페이스의 마샬링 • 운영체제에 의해 제공(ole32.dll) • 자동화 인터페이스의 마샬링 • 운영체제에 의해 제공(oleaut32.dll) • 커스텀 인터페이스의 마샬링 • MIDL 컴파일 결과로 만들어지는 코드 이용 • IDL 작성 시 속성 지정

  45. 로컬 서버의 구현 • 인-프로세스 서버와는 달리 CoInitialize, CoUninitialize 호출 • DllRegisterServer, DllUnregisterServer • 명령행 인자 처리로 구현 • /RegServer, /UnregServer 옵션 처리 • DllGetClassObject • CoRegisterClassObject, CoRevokeClassObject 이용 • DllCanUnloadNow • 스스로 능동적으로 종료

  46. CoRegisterClassObject (1) • COM은 내부적으로 등록된 클래스 팩토리 COM 객체를 저장하는 ROT 관리 • 클라이언트가 CoGetClassObject 호출 시 ROT부터 검사함 • 등록이 안된 경우 /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 클래스

More Related