*이 포스팅은 개인 학습을 위해 내용을 정리한 것이 목적입니다.
COM은 클라이언트와 컴포넌트의 완벽한 분리를 위해, 클라이언트에서 컴포넌트의 의존성을 완전히 배제시킨다.
COM에서 중요한 것은 AddRef(), Release(), QueryInterface()이다. AddRef()는 컴포넌트 생성 시, 카운트를 하나씩 증가, Release()는 컴포넌트의 작업이 완전히 끝나면 하나 감소시킨다. 컴포넌트의 삭제를 직접 하는 것이 아니라, 컴포넌트에게 정보를 주어, COM이 스스로 생명주기를 결정하게 한다. 또한 QueryInterface()를 통하여 유일성을 보장받는다.
1. 참조카운터의 모든 것
COM컴포넌트의 생명주기를 클라이언트에서 관리하지 않고, COM객체에 맡김.
컴포넌트 생성시 AddRef()로 카운트를 증가시키고, Release()로 카운트를 감소시킨다.
1)참조 카운터 기술
AddRef(), Release()를 이용하여 컴포넌트가 스스로 메모리에서 해제될 지, 현재 상태를 유지할지에 대한 정보를 준다.
* 참조 카운터 사용에 필요한 규칙
i) 임의의 함수가 인터페이스 포인터를 얻고자 할 때에는, 함수가 반환되기 전에 AddRef()를 반드시 호
출해야 한다. QueryInterface()나 CreateInstance()도 마찬가지임. 인터페이스 사용이 끝날 때에는
Release()가 호출되어야 한다.
ii) 인터페이스 포인터가 다른 인터페이스 포인터에 부여될 때에도, AddRef()함수를 호출해야 한다.
2) AddRef() / Release() 함수의 구현.
위 함수 구현은 참조 개수의 증가/감소만 수행하면 된다.
ULONG __stdcall AddRef(){ return ++m_cRef; } ULONG __stdcall Release(){ if( --m_cRef == 0){ delete this; //참조카운트가 0이면 컴포넌트를 삭제한다. return 0; } return m_cRef; } |
위 함수는 Win32에서는 InterlockedIncrement()와 InterlockedDecrement()로 사용된다. 또한 단일 스레
드에서만 사용이 가능하다.
*전체 예제
2. HRESULT의 모든것
COM사용에서 반드시 알고 있어야 할 반환 값의 타입. HRESULT는 오류코드에 대한 정보 외에도 사용자 정의 코드를 만들 수 있는 기능을 제공한다. HRESULT는 32비트 값으로 다음3개 필드로 구성됨
i) Severity Code (30, 31비트)
- HRESULT의 성공/실패 상태를 담음.
ii) Facility Code (16~27비트)
- 반환코드 값 형식에 대한 정보 수록
iii) Return Code ( 0~15비트)
- 함수의 반환 값에 대한 정보가 들어있다.
이 HRESULT를 바로 다루는 것 보다 자주 사용되는 매크로를 이용하는 것이 낫다.
- 자주 사용되는 매크로
이름 |
설명 |
FAILED() |
함수의 수행이 실패했을 때 TRUE, 그 외에는 FALSE |
SUCCEEDED() |
함수의 수행이 성공했을 때 TRUE, 그 외에는 FALSE |
IS_ERROR() |
Severity()코드가 오류를 가리키면 Error반환 |
HRESLUT_SEVERITY() |
주어진 HRESULT에서 Severity코드를 추출하여 반환 |
HRESULT_FACILITY() |
주어진 HRESULT에서 Facility코드를 추출하여 반환 |
HRESULT_CODE() |
주어진 HRESULT에서 반환코드를 추출하여 반환 |
MAKE_HRESULT() | Severity, Facility, Code를 조합하여 HResult를 새로이 생성 |
- 자주 사용되는 반환값
이름 |
코드 |
의미 |
S_OK |
0x00000000L |
함수가 성공적으로 수행됨. 값은 0 |
NOERROR |
S_OK와 동일 | |
S_FALSE |
0x00000001L |
함수는 성공적으로 수행했으나 리턴값은 false.값은 1 |
E_UNEXPECTED |
0x8000FFFF |
예상치 못한 에러 |
E_NOTIMPL |
0x80004001L |
멤버함수가 구현되지 않음 |
E_NOINTERFACE |
0x80004002L |
컴포넌트가 요청한 인터페이스를 지원하지 않음 |
E_OUTOFMEMORY |
0x8007000EL |
컴포넌트가 필요로 하는 메모리가 부족하다 |
E_FAIL |
0x80004005L |
규격화 되지 않은 에러 |
E_INVALIDARG | 0x80070057L | 메소드에 잘못된 인수가 넘어옴 |
E_ACCESSDENIED | 0x80070005L | 요청한 컴포넌트에 접근할 수 없음. |
(CBD, Component Development with Visual C++,ATL 일부 발췌)
- Facility코드.
HRESULT값이 어느 그룹에 속해있는지에 대한 정보를 가지고 있음.
코드이름 |
값 |
설명 |
FACILITY_CONTROL |
10 |
OLE컨트롤과 관련된 error값 |
FACILITY_SSPI |
9 |
OLE 구조화 저장소 인터페이스에 관련된 HRESULT로 사용하기 위해 예약 |
FACILITY_WINDOWS |
8 |
다른사항에 해당되지 않는 모든 에러코드를 그룹화 |
FACILITY_WIN32 |
7 |
Win32API에러 코드를 HRESULT형태로 그룹화 |
FACILITY_ITF |
4 |
커스텀 인터페이스의 경우 이 Facility코드를 사용해야 HRESULT를 반환. 예외상황을 의미 |
FACILITY_STORAGE |
3 |
OLE 구조화 저장소 인터페이스에 관련된 HRESULT로 사용하기 위해 예약 |
FACILITY_DISPATCH |
2 |
OLE자동화에 관련된 HRESULT를 사용하기 위해 예약 |
FACILITY_RPC |
1 |
RPC에 관련된 HRESULT로 사용하기 위해 예약 |
FACILITY_NULL |
0 |
일반적인 목적으로 사용하는 HRESULT |
1) 사용상의 주의점
컴포넌트가 리모트로 동작하게 되면 반환되는 에러코드의 경우도 바뀐다.
2) 사용자 정의 코드
자신만의 코드를 정의하려면 MAKE_HRESULT()매크로를 이용하여 만들것. 지켜야 할 규칙
i) 0x0000에서 0x01FF사이의 값을 사용하지 않음. (COM이 사용하는중)
ii) FACILITY_ITF는 사용자 인터페이스에서 발생하는 코드이므로, 특수한 인터페이스를 위한 에러코드를
많이 작성하지 않는 편이 좋다.
3. GUID의 모든 것
1) GUID
컴포넌트와 인터페이스를 정의하는데 사용하는 숫자. 각 인터페이스는 유일한 GUID를 가진다.
GUID는 128비트의 구조체이다. 구조체는 아래와 같이 정의되어 있다.
typedef struct _GUID { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; } GUID; |
2) GUID를 사용하는 이유
모든 인터페이스는 유일성이 보장되어야 한다. 유일성이 보장되지 않는다면, 어느 인터페이스를 호출할
지 결정할 수 없다.
GUID의 유일성을 보장하기 위해 MS에서는 클라이언트 네트워크 카드의 정보를 이용한다. (세상에서 유
일하게 겹쳐지지 않는 번호임)
3) GUID의 생성
UUIDGEN이나 GUIDGEN으로 생성하면 된다.
- DEFINE_GUID매크로
헤더파일에 GUID를 선언하면, 헤더파일이 중복되었다는 에러가 발생한다. 따라서 하나의 소스파일에
서만 초기화 할 수 있도록 하는 매크로. 이 매크로는 initguid.h가 포함된 경우에만 DEFINE_GUID는 초
기화가 수행된다.
내용 출처 : COM Bible
댓글