COM (component object model)

C++ 2010. 11. 2. 05:02
COM (component object model)




COM 관련 용어

COM 프로그래밍 지원 라이브러리
MFC(Microsoft Foundation Class)
ATL(ActiveX Template Library) : MFC의 경량화 정도임.
CoClass (Component Object Class)
하나 이상의 인터페이스와 구현 코드를 갖는 클래스로 
IUnknown 인터페이스를 상속받아 AddRef(), Release(), QueryInterface()를 구현.
COM 서버
CoClass들이 모여서 만들어진 DLL, EXE에 저장되는 단위
종류 : In-Process 서버, Out-Process 서버
In-Process   서버 : DLL로 생성. 클라이언트와 같은 메모리 사용
빠른 속도. 같은 메모리 공간 사용의 위험.
Out-Proecess 서버 : EXE로 생성. 클라이언트와 별도 메모리 사용. 
로컬서버와 리모트서버(DOM: Distributed COM)의 두가지 종류를 갖음.
클라이언트와 데이터 공유시 Proxy, Stub을 사용하는 마샬링 과정 필요.
함수호출 속도 저하. 메모리 공간 분리로 위험성 최소화.
COM은 Reference Count를 통해 모든 클라이언트가 메모리 해제를 요청한 시점에 실제 메모리를 해제한다.
(AddRef(), Release()에서 Reference Count를 카운팅)
COM서버에 DllCanUnloadNow()를 구현하면 운영체제는 이를 호출하여 COM 서버 종료 여부를 판단.

CLSID
CoClass의 GUID.
GUID 생성 : guidgen.exe(C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools)
CLSID와 CoClass의 매핑 정보는 레지스트리에 저장됨.
EXE의 경우
프로그램 실행시 마다 CoRegisterClassObject()를 호출하여 CoClass와 CLSID를 레지스트리에 저장.
DLL의 경우
DllRegisterServer(), DllUnregisterServer()로 레지스트리 등록/해제를 구현하면
regsvr32.exe로 DLL 등록시 DLL 안의 DllRegisterServer()를 호출하며 
regsvr32.exe로 DLL 해재시 DLL 안의 DllUnregisterServer() 호출.
ex] 등록 : c:\> RegSvr32 XXXXX.dll
     해제 : c:\> RegSvr32 /U XXXXX.dll

IID
인터페이스의 GUID

Class Factory
CoClass를 효율적으로 생성하기 위한 CoClass
(MFC의 경우 COleObjectFactory())
IUnknown을 상속받은 IClassFactory 를 정의하고 CreateInstance(), LockServer()를 구현함.
COM 응용 사례
Automation, OLE 문서, 클림보드, 드래그 앤 드롭, ActiveX 컨트롤.. 등.





COM 서버 작성 절차

1. "MFC > MFC DLL" project 생성

2. guidgen.exe 로 GUID 생성하여 CLSID, IID 선언

3. 레지스트리 등록/해제를 위한 
   DllRegisterServer(), DllUnregisterServer() 구현

4. IUnknown을 상속한 인터페이스 작성
ex)
struct ICalculator : public IUnknown
{
virtual HRESULT __stdcall Add(int a, int b, int* rst) = 0;
}

5. IUnknown을 상속한 인터페이스를 구현하여 CoClass 작성
구현 대상 : 
AddRef() : reference counter 증가
Release() : reference counter 감소
QueryInterface() : 인터페이스 포인터 획득
사용자 정의 함수 : 구현할 기능

6. IClassFactory를 구현하여 Class Factory 작성.
구현 대상 : 
AddRef() : reference counter 증가
Release() : reference counter 감소
QueryInterface() : 인터페이스 포인터 획득
CreateInstance() : 주어진 IID에 해당하는 COM오브젝트 생성 및 포인터를 client에게 전달하는 함수
LockServer() : client가 직접 클래스 팩토리 메모리를 해제할지 여부를 지정할지 여부 구현.
사용자 정의 함수 : 구현할 기능 

7. DllGetClassObject() 작성
클래스 오브젝트 생성 및 인터페이스 포인터를 client에게 전달하는 함수

8. COM 서버의 reference count
더이상 COM 오브젝트가 사용되지 않을 경우 COM 서버 삭제.
전역 카운터 변수 정의 및 DllCanUnloadNow() 함수 정의
DllCanUnloadNow() : OS에서 DLL 메모리 해제 확인을 위해 호출하는 함수
전역 카운터 변수는 CoClass와 ClassFactory 클래스의 생성자와 소멸자에서 카운팅.
9. DEF 파일 편집
Dll관련 함수를 OS에서만 호출 할 수 있도록 private로 지정
ex)
EXPORTS
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
   ; Explicit exports can go here

10. 컴파일 및 등록
c:\> RegSvr32.exe XXXX.dll
(성공 메시지 창을 보기 싫다면 "RegSvr32.exe /S XXXX.dll"
**. 참고 : regsvr32.exe 로 등록된 DLL 확인
regedit에서 HKEY_CLASSES_ROOT\\CLSID\\ 에서 직접 찾기 힘드니 아래 프로그램 이용 

RegDllView (윈도우 프로그램 : 편리함)
ListDlls (콘솔 프로그램:dump를 뜸)
http://technet.microsoft.com/ko-kr/sysinternals/bb896656(en-us).aspx  




COM 클라이언트 사용 과정

1. COM 오브젝트 생성 : CoGetClassObject(CLSID), CoCreateInstance(CLSID)
    client는 CoGetClassObject(CLSID)로 COM 오브젝트 생성하며
    클래스 오브젝트의 인터페이스 포인터를 받음.
    (COM 라이브러리는 CLSID로 레지스트리를 뒤져 COM 서버를 메모리에 올리고 
     COM 서버의 DllGetClassObject() 호출하며 DllGetClassObject()는 
     클래스 오브젝트 생성 및 인터페이스 포인터를 client에게 전달)

2. 인터페이스 포인터 획득 : QueryInterface()
    CoClass 오브젝트의 인터페이스 포인터로 
    서버의 CreateInstance(IID) 호출 및 COM 오브젝트 포인터를 받음.
    (CreateInstance()는 주어진 IID에 해당하는 COM 오브젝트 생성 및 포인터를 client에게 전달)

3. COM 오브젝트의 포인터로 COM 기능 사용.

4. COM 오브젝트 메모리 해제 : Release()





COM 클라이언트 작성 절차

1. COM 라이브러리 초기화
CoInitialize(NULL);

2. COM 서버와 동일한 CLSID, IID 선언

3. COM 서버의 IUnknown 인터페이스 상속한 사용자 정의 인터페이스 정의

4. class factory 사용시 
CoGetClassObject() -> CreateInstance() -> 사용자 정의 함수()
   class factory 미사용시
CoCreateInstance() -> 사용자 정의 함수()

5. COM 라이브버리 해제
CoUninitialize();

COM 서버/클라이언트 작성 샘플

추가된 메소드는 ICalculator의 Add() 뿐임.
COM 서버 샘플
MFC DLL 프로젝트 생성 후, ComServer.cpp 파일에 모든 코드 작성함. (샘플로 작성한거라...)

COM 클라이언트 샘플
Console project로 작성.

COM 서버 샘플 preview
// {54CFE99A-9C45-4666-929C-D28AA74AADCD}
static const CLSID CLSID_ICalculator = { 0x54cfe99a, 0x9c45, 0x4666, { 0x92, 0x9c, 0xd2, 0x8a, 0xa7, 0x4a, 0xad, 0xcd } };

// {F4701020-C489-474f-BE2C-648FF41270CD}
static const IID IID_ICalculator = { 0xf4701020, 0xc489, 0x474f, { 0xbe, 0x2c, 0x64, 0x8f, 0xf4, 0x12, 0x70, 0xcd } };

struct ICalculator : public IUnknown {
virtual HRESULT __stdcall Add(int a, int b, int* rst) = 0;  //사용자 정의 함수
};

class CCalculatorImpl : public ICalculator {
public:
CCalculatorImpl();
virtual ~CCalculatorImpl();
//IUnknown
ULONG __stdcall AddRef(); //reference counter 증가
ULONG __stdcall Release(); //reference counter 감소
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); //인터페이스 포인터 획득

//ICalculator
HRESULT __stdcall Add(int a, int b, int* rst); //사용자 정의 함수
protected:
ULONG m_uRefCount; //Reference counter
};
CCalculatorImpl::CCalculatorImpl()
CCalculatorImpl::~CCalculatorImpl()
ULONG CCalculatorImpl::AddRef() //reference counter 증가
ULONG CCalculatorImpl::Release() //reference counter 감소 및 COM 오브젝트 메모리 해제
HRESULT __stdcall CCalculatorImpl::QueryInterface(REFIID riid, void** ppv) //CoClass 오브젝트의 인터페이스 포인터 획득
HRESULT __stdcall CCalculatorImpl::Add(int a, int b, int* rst) //사용자 정의 함수 (덧셈)

class CCalculatorClassFactory : public IClassFactory {
public:
CCalculatorClassFactory();
virtual ~CCalculatorClassFactory();

//IUnknown
ULONG __stdcall AddRef(); //reference counter 증가
ULONG __stdcall Release(); //reference counter 감소
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); //인터페이스 포인터 획득

//IClassFactory
HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv);
HRESULT __stdcall LockServer(BOOL fLock);

protected:
ULONG m_uRefCount; //reference counter
};
CCalculatorClassFactory::CCalculatorClassFactory()
CCalculatorClassFactory::~CCalculatorClassFactory()
ULONG __stdcall CCalculatorClassFactory::AddRef() //reference counter 증가
ULONG __stdcall CCalculatorClassFactory::Release() //reference counter 감소 및 COM 오브젝트 메모리 해제
HRESULT __stdcall CCalculatorClassFactory::QueryInterface(REFIID riid, void** ppv) //CoClass 오브젝트의 인터페이스 포인터 획득
HRESULT __stdcall CCalculatorClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv) //주어진 IID에 해당하는 COM 오브젝트 생성 및 포인터를 client에게 전달
HRESULT __stdcall CCalculatorClassFactory::LockServer(BOOL fLock) //client가 명시적으로 클래스 팩토리를 메모리 해제할지 말지를 지시할 수 있도록 구현함.

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) //클래스 오브젝트 생성 및 인터페이스 포인터를 client에게 전달하는 함수(COM 라이브러리에서 호출)
STDAPI DllRegisterServer() //레지스트리 등록을 위한 함수(RegSvr32.exe 로 dll 등록시 호출)
STDAPI DllUnregisterServer() //레지스트리 해제을 위한 함수(RegSvr32.exe /U로 dll 등록 해제시 호출)
STDAPI DllCanUnloadNow() //COM 서버 종료 여부 판단을 위해 호출하는 함수(OS에서 호출) 












'C++' 카테고리의 다른 글

ATL (Active Template Library)  (0) 2010.11.04
ActiveX control  (0) 2010.11.04
OLE (Object Linking and Embedding)  (0) 2010.11.04
Automation (자동화)  (0) 2010.11.02
C++ 복사 생성자(copy constructor)와 대입 연산자(substitution operator)  (0) 2010.11.01
STL (Standard Template Library)  (0) 2010.11.01
C++ 파일과 콘솔 출력 예제  (0) 2010.11.01
Registry 레지스트리  (0) 2010.10.27