*이 포스팅은 개인 학습을 위해 내용을 정리한 것이 목적입니다.
크게 COM 컴포넌트의 구현은 다음의 4과정으로 구분할 수 있다.
1) COM인터페이스 정의
2) COM 객체클래스 구현
3) 클래스팩토리 클래스 구현 <- 여기까지 이번 chapter
-----------------------------------------------------------
4) COM 컴포넌트 구현 <- 여기는 다음 chapter
3.1 COM인터페이스 정의
- 인터페이스 : 클라이언트와 서버컴포넌트가 서로 커뮤니케이션하기 위한 규약.
C++언어에서 순수 가상함수만을 포함하는 추상클래스로 표현. 즉, 가상함수테이블 메모리구조를 가짐
- 추상클래스를 사용하지 않는 이유 : 언어에 독립적이어야 한다. 그래서 IDL을 사용
- COM/DCOM은 IDL과 인터페이스를 통해 컴포넌트가 언어독립적인 특징을 갖도록 하는 것임
3.1.1 표준 인터페이스
- COM에는 두 종류의 인터페이스가 있다. 표준 인터페이스와 커스텀인터페이스
1) 표준인터페이스 : COM에 미리 정의되어 있음. IUnknown, IClassFactory, IDispatch, IOleControl등
2) 커스텀인터페이스 : 사용자가 정의한 인터페이스
3.1.2 커스텀 인터페이스 정의
- 커스텀 인터페이스 정의를 위해서는 IDL을 사용해야 함.
- COM에서 사용하는 IDL은 OSF(open source foundation)와 RPC(remote procedure call)에서 사용하
던 IDL을 확장한 것으로 C++과 구문이 유사하다.
- 각 인터페이스는 헤더(attribute정보 포함)와 본체(인터페이스 정의 구문)로 구성된다.
//MyClass.idl //인터페이스헤더 [ object, //IMyClass인터페이스가 COM인터페이스임을 의미 uuid( $UUID ) // IMyClass인터페이스의 IID ] //인터페이스 본체 interface IMyClass : IUnknown { import "unknwn.idl"; HRESULT Func([in, string]wchar_t* val, [out, string]wchar_t** msg); }; |
> in : COM객체로 이동하는 인수를 의미, out : COM객체가 반환하는 인수를 의미
3.1.3 MIDL컴파일러
- 정의된 IDL구문은 MIDL컴파일러를 이용하여 컴파일 된다. 만약 myTest.idl을 컴파일 하면,
myTest.h //C/C++에서 사용할 수 있는 인터페이스 클래스의 코드가 정의됨 myTest_i.c // IDL파일에 정의된 모든 IID를 정의한 코드가 저장됨 myTest_p.c // 이 파일과 dlldata.c에는 커스텀 인터페이스의 마샬링 코드가 정의된 프록시/스텁 dlldata.c // 코드가 저장됨 |
위와 같이 파일들이 나온다. (커맨드 라인에서 입력하려면 VS의 명령프롬프트를 이용하자.)
- __stdcall 키워드
i) VisualC++의 확장키워드. 해당 함수가 파스칼 호출 규약을 사용하게 한다.
* 파스칼호출규약 : 호출된 함수가 리턴되기 전에 스스로 생성된 매개변수 삭제
ii) C++규약은 파스칼 호출규약과 다르게 함수를 호출한 함수에서 함수 호출 후 삭제
iii) COM인터페이스에 포함되는 모든 메소드는 파스칼 규약 준수.
*함수호출규약
호출규약 |
인수전달 |
스택정리 |
name mangling규칙 |
__cdecl |
오른쪽 먼저 |
호출자 |
_함수명 |
__stdcall |
오른쪽 먼저 |
함수 |
_함수명@인수크기 |
__fastcall |
ECX,EDX에 우선전달, 나머진 오른쪽먼저 |
함수 |
@함수명@인수크기 |
thiscall |
오른쪽먼저, this는 ecx레지스터로전달 |
함수 |
C++네이밍 |
naked |
오른쪽먼저 |
함수 |
없음 |
c방식 함수 호출과, pascal방식의 함수 호출의 차이
(pascal은 win16에서, win32에서는 가변매개인자 함수를 제외하곤 __stdcall임)
규약 | 인수를 스택에 집어넣는 순서 | 스택정리 |
pascal |
왼쪽->오른쪽 |
피호출자 |
cdecl |
오른쪽->왼쪽 |
호출자 |
stdcall |
오른쪽->왼쪽 |
피호출자 |
(참조 : http://blog.mosaicstory.net/m/post/93#)
3.2 COM객체클래스 구현
- COM객체가 인터페이스를 구현하는 방법 2가지
i) 인터페이스 구현 클래스를 포함하는 방식으로 구현
ii) COM객체가 인터페이스를 상속하는 방법으로 구현
3.2.1 인터페이스 포함 방법
- 구문은 다소 복잡하지만 디버깅이 쉽다.
3.2.2 인터페이스 상속 방법
- COM객체 클래스를 인터페이스 상속 방법으로 구현하는것은 쉽다. 인터페이스에서 파생하는 COM객
체 클래스를 정의하면 됨
3.2.3 다중인터페이스 구현 - 인터페이스 포함방법
- 하나의 COM객체가 여러 인터페이스를 통해 서비스를 제공하는 경우, 인터페이스 포함방법으로 구현
하면 인터페이스별로 각기 다른 인터페이스 포인터 주소를 가진다. 그래서 요청한 인터페이스에 대한
주소값이 다 다르다(디버깅시 유리). 그러나 포함된 클래스마다 IUnknown인터페이스를 모두 구현.
3.2.4 다중인터페이스 구현 - 인터페이스 상속방법
- 노출하고자 하는 인터페이스에서 다중상속되는 COM클래스를 정의하면 됨. 단, 다른 인터페이스의 경
우 주소가 다르다. 강제 형 변환을 해 주어야 하지만, 최근에는 컴파일러에서 지원.
3.2.5 CLSID정의
- 클래스별로 고유한 CLSID를 가지게 하기 위해, GUIDGEN툴을 이용해서 구하면 됨.
3.3 클래스팩토리 클래스 구현
3.3.1 왜 클래스 팩토리를 구현해야 하는가?
- 클래스 팩토리 : COM클래스의 인스턴스를 생성하는 공장으로, 또하나의 COM객체.
> CreateInstance()를 호출될 때, 이미 해당 클래스팩토리 COM객체가 결정되었다. 1팩토리 1객체.
3.3.2 클래스팩토리 COM객체 구현
- IClassFactory를 상속받은 클래스를 정의하고, IUnknown의 3개의 메소드 및 CreateInstance()와
LockServer()메소드를 구현하면 된다.
주의할 것은 IClassFactory를 상속받으려면 unknwn.h를 포함할것. 안하면 컴파일 안됨.
예)
class CMyFactory : public IClassFactory{ public: CMyFactory(); ~CMyFactory(); //IUnknown methods HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); //IClassFactory methods HRESULT STDMETHODCALLTYPE CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID* ppv); HRESULT STDMETHODCALLTYPE LockServer(BOOL bLock); private: DWORD m_cRef; } |
해당 코드의 세부 구현은 chapter 4에서 다룬다. 다만 여기서, CreateInstance만 확인
예제
CMyFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID* ppv){ HRESULT hr = E_FAIL; CHello* pHello = NULL; *ppv = NULL; if( pUnkOuter != NULL){ hr = CLASS_E_NOAGGREGATION; } else{ pHello = new CHello; if(pHello != NULL){ hr = pHello->QueryInterface(riid, ppv); if(FAILED(hr)){ delete pHello; } } else { hr = E_OUTOFMEMORY; } } return hr; } |
> 여기에서 pUnkOuter가 != NULL인 경우 처리가 별도로 되어 있다. 원래 pUnkOuter는 NULL이 들어온다. 단, COM서버를 통합하는 경우에만 pUnkOuter가 NULL이 아니다. ( http://gomnezip.tistory.com/329 참조)
클래스팩토리의 동작에 관한 설명도 다른 포스팅에 있다. 그리고 추후에 다시 다룰예정.
출처 - CBD, Component Development with Visual C++,ATL
댓글