1. 자동화 객체란?
1) 자동화의 장, 단점
- 스크립트 언어를 사용하는 클라이언트 어플리케이션에서 COM객체를 사용하려면 자동화가 지원되야함
- 아웃오브프로세스의 경우 COM객체는 마샬링을 수행하는 proxy/stub DLL이 필요하다. 자동화를 사용
하여 마샬링코드 작성과 proxy/stub DLL을 클라이언트에 분배할 필요가 없음.
- 단점 : 실행속도가 느리다. 직접적인 COM인터페이스로의 접근이 아니라 Invoke()를 이용한 간접 접근
-> MS에서는 이중 인터페이스 지원을 권장. (직접호출 및 자동화 지원)
2) 자동화 구현을 위한 조건
- 자동화 구현을 위해 형식 라이브러리와 IDispatchImple에 인터페이스 관련 정보가 포함되어야 함
- ATL에서는 IDispatchImple에서 파생되는 COM객체를 생성한다. IDispatchImple은 IDispatch의 기본
부분을 구현해줌. 개발자는 구현하는 메소드나 속성만 구현하면 된다.
- DispatchImpl템플릿을 선언한 경우, IDL파일에서는 반드시 dual키워드를 입력하고 인터페이스를
IDispatch에서 파생해야 하고, dispID를 추가.
[ object, uuid( UUID ), dual, helpstring("String"), pointer_default(unique) ] interface IMyClass : IDispatch { [propput, id(1), helpstring("HELPSTRING")] HRESULT func([in] param1 ... ); }; |
또한, 다음과 같은 COM MAP을 제공
BEGIN_COM_MAP(CMyClass) COM_INTERFACE_ENTRY(IMyClass) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() |
COM_INTERFACE_ENTRY(IDispatch)는 표준 디스패치 인터페이스를 통해 자동화 객체에 접근할 때 사
용하는 인터페이스를 정의함
2. 디스패치 인터페이스
- 디스패치 인터페이스는 IUnknown로부터 파생된다.
IUnknown인터페이스 멤버에 추가하여 GetTypeInfoCount(), GetTypeInfo(), GetIDsOfNames(), Invoke()
4개의 멤버함수를 포함한다.
1) IDispatch::GetIDsOfNames()
자동화 객체가 제공하는 메소드 또는 속성에 접근할 때, 이 함수를 호출하여 해당 메소드나 속성의 이름에
해당하는 dispID값 구한다.
HRESULT GetIDsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId); |
riid는 예약어로서 IID_NULL을 입력. 두번째 매개변수인 rgszNames는 구하려는 속성, 메소드 명을 배열
형태로입력. 세번째에는 사용된 배열의 개수, 네번째는 로케일정보, 마지막변수는 dispID를 반환
아래는 일반적인 호출 방법
szName = L"MyFunc"; m_pDisp->GetIDsOfNames(IID_NULL, &szNAme,1 LOCALE_SYSTEM_DEFAULT, &m_dispID); |
2) IDispatch::Invoke()
원형
HRESULT STDMETHODCALLTYPE Invoke( DISPID dispIdMember, // DISP ID REFIID riid, // IDD_NULL LCID lcid, // 로케일 정보 WORD wFlag, // flag DISPPARAMS *pDispParams, // 매개변수 정보 VARIANT *pVarResult, // 리턴된 결과 EXCEPTINFO *pExcepInfo, // 예외사항 정보 UINT *puArgErr // 에러 매개변수 인덱스 ); |
함수 호출 예
m_pDisp->Invoke(m_dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &m_dispparams, &m_retVal, NULL, NULL); |
i) DISPID : 자동화 컨트롤러가 호출하기 원하는 함수의 DISP ID값
ii) REFIID : 향후 사용을 위해 예약. 현재는 IID_NULL
iii) LCID : 로케일 정보
iv) FLAG : dispID에 대한 플래그. 각각 다음과 같다
DISPATCH_METHOD : 멤버 함수를 메소드로서 호출
DISPATCH_PROPERTYGET : 속성 또는 데이터 멤버를 읽는 함수
DISPATCH_PROPERTYPUT : 속성 또는 데이터 멤버를 저장하는 함수
DISPATCH_PROPERTYPUTREF : 값보다는 레퍼런스 정보를 부여해, 속성 혹은 데이터멤버를 저장
v) DISPPARAMS : Invoke사용시 메소드 또는 속성에 대응하는 인수들은 DISPPARAMS구조체에 의해 전
달된다. 이 구조체는 Variant인수 배열에 대응하는 포인터와 DISPID의 배열에 대응하는 포인터로 구성
typedef FARSTRUCT tagDISPPARAMS( VARIANTARG FAR* rgvarg; //매개변수 배열 DISPID FAR* rgdispidNameArgs; //명명된 인수의 디스패치 ID unsigned int cArgs; //매개변수의 개수 unsigned int cNamedArgs; // 명명된 매개변수의 개수 } DISPPARAMS; |
vi) VARIANT *pVarResult
Invoke()함수에 의해 실행된 메소드나 Get속성의 결과를 저장하는 VARIANT포인터. 결과값 리턴이 없거
나 PUT속성에는 NULL값을 지정
vii) EXCEPINFO
Invoke()에 의해 실행된 메소드나 속성이 예외 상황을 만났을 때, 해당 예외 상황에 대한 정보가 저장되
는 구조체의 포인터
viii) UINT *puArgErr
rgvarg에 포함된 첫 번째 매개 변수에 에러가 발생하였을 경우에 발생한다.
3) 에러 정보 처리
만약 Invoke()를 사용할 수 없을 경우 어떻게 추가적인 에러 정보를 받을 수 있을까? (Invoke()의 경우 에러 정보를 EXCEPINFO구조체를 통해 많은 정보값을 받아올 수 있다.)
- IErrorInfo( Get..으로 시작하는 함수들의 집합)와 ICreateErrorInfo( Set.. 으로 시작하는 함수들의 집합)
출처 - COM Bible
댓글