COM, ATL

COM 이란 ... 참고자료....좀 잘못된 부분도 있는듯 하다

디버그정 2008. 8. 14. 20:05
  • COM 이란..

    • Component Object Model . 개체를 만드는 COM 규칙. Spec!
    • COM을 사용하는 이점.

      • Location Transparency (위치 투명성) - Client가 Server개체를 만들떄 필요한 dll의 Registry위치는 COM SS이 알아서 가져다 주므로 공개되지 않는다.
        RPC  - Remote Proceser call   / LRPC - Light  Remote Proceser call => 모두 COM SS 내부에서 ...
      • Language Independence (언어 독립성) - Client가 Visual Basic이고 Server가 C++이 더라도 개체생성 코드가 바뀌지 않는다.(다른 언어에 대해서도 부담이 전혀 없다.) 어떤 언어든 COM의 규칙에 따라서만 만들면 된다.
      •  Server에 보내는 UDP패킷 처리등은 모두 COM SS이 알 아서 처리한다.
    • Marshaling - 함수 호출등을 패킷으로 싸서 보내는것.
      Unmarshaling - 패킷을 푸는 것.(인자 등..)


  • COM object

    • 모든 개체는 복수의 인터페이스를 가져야 한다.( 각 인터페이스마다 함수가 딸리는 구조.) 
    • 인터페이스는 함수의 집합으로 구성. 
    • C++의 객체가 변수(멤버)를 내포하고 있는데 반해, COM object는 변수를 내놓지 않는다. 

  • DLL일 경우는 같은 주소를 가르키므로 큰 문제가 없지만(directed pointer), EXE일 때는 프로세스 공간이 다르므로 차이가 있다.

    • dll - Directed Pointer           => load library
    • exe -  Proxy Pointer

      • Proxy - Client의 주소 영역에 있으며 Marshaling 
      • Stub - Server의 주소 영역에 있으며 UnMarshaling
      • Proxy와 Stub은 원칙적으로 COM SS 에서 제공하는것이 아니라 직접 구현해야 하지만,
        MIDL을 이용해서 구현할 수 있다. 
      • IDL(Interface Definition Language)로 .idl파일을 생성 -> midl.exe로 컴파일. -> 4개의 파일이 자동생성 -> 묶어서 컴파일 하면 PS.dll생성. -> PS.dll은 Proxy와 Stub에 대한 코드를 가진다.
      • dll의 경우는 IDL이 필요없다는 거!! 

  • DCOM - 다른 컴퓨터에 있는 개체를 access하는 범위.

    • DLL - inprocess server  
    • EXE - local  server
    • DCOM - remote server


 

  • COM에서 ID는 128비트 
  • UUID(=GUID) 
  • PROXY도 COM OBJ이며 클라이언트와 같은 주소공간에 있다. 
  • Marshaling을 한다고 Proxy는 아니다. stub도 Marshaling을 할 수 있다. 
  • COM SS를 사용하기 위해서는 CoInitializ0e(UULL)을 꼭 해 줘야한다. 사용후엔 CoUninitialize(); 

  • IDL

    • IDL 은 각 언어에 맞는 헤더파일을 만들 수 있다.(생성되는 4개의 파일 중 하나가 .h파일) 
    • 서버에서 만들어낸 .h파일이나 IDL을 클라이언트로 넘겨 준다. 언어가 다르다면 IDL을 넘겨주어야 한다. (헤더파일은 언어의존적이기 때문에) 

  • OLE

    • Object Linking & Embedding 
    • Compiund Document!! 복합문서를 만드는 기술. 
    • COM은 OLE의 연장선상에 있다.

      • Embedding - ex) Word안에 Excel이 들어가게 하는 기술 
      • In-Place activation -  ex) Word안에 Excel을 그 자리에서 편집하는 기술 
      • OLE-Automation - ex) COM에서 VB이 C++로 만든 COMobject를 로드할때 사용 하는 기술(Automation)
      • OLE-Document - 복합문서
      • Structured storages - 복합문서에 Data를 효과적으로 저장하기 위해 필요한 기술.
  • Open Software Form
  • RPC
  • Inter-computer communication
  • 메모리 구조. Virtual Fuction Table과 COM interface는 binary code 모양이 동일하다.
  • Interface

    • 표준 인터페이스(추가 불능, IUnknown) / 커스텀 인터페이스(추가용) 가 있다. 
    • 모든 COM interface는 IUnknown 인터페이스에서 파생되어야 한다. 
    • QueryInterface로 질의. 
    • AddRef - 개체에 대한 레퍼런스 카운트 증가.(명시적)

      • CocreateInstance , Qurey에도 레퍼런스 카운트는 증가.(묵시적) 
    • Release() - 개체에 대한 레퍼런스 카운트 감소. 
    • Interface를 가져오는 방법

      • CocreateInstance
      • QueryInterface


  • QueryInterface

    • 모든 인터페이스는 IUnknown인터페이스에서 상속받기 때문에.. QueryInterface로 어떠한 인터페이스로도 변할수있다. 
    •  IUnknown * PI;
      PI->QueryInterface( IID_IX,(void**)&pIX);
    • 어떤 인터페이스든지 얻어오고 QueryInterface할수 있다. 
    • 리턴값 : 존재하면 S_OK, 없으면 E_NOINTERFACE 

SERVER
  • QueryInterface의 구현

    • 대부분 If-then-else
    • 내부적으로 Addref()의 호출.  
    •  ATL 은 이미 구현을 제공.
  • 다중상속

    • (IX*)CA::this / (IY*)CA::this - 가상함수 테이블이 IX / IY 것으로 나뉨. 
  • Reference Counting (Lifetime 제어)

    •  인터페이스를 리턴하는 함수는 반드시 Addref()를 호출해야한다.

      • 대표적으로 CocreateInstance()와 QueryInterface() 
    •  종료시 인터페이스에 대하여 Release()를 호출

      • 초기화되거나 할당된 자료구조는 인터페이스 별로 할당 되므로 사용한 인터페이스마다 따로 Release해야 함. 
  • Class object(=Class Factory)

    • 모든 개체 각각마다 Class Object가 존재한다. 
    • IClassFactory등을 내놓고 있는 COM Object 
    • 특정 타입의 개체를 찾거나 생성하는 기능을 담당한다. 
    • IClassFactory

      • 멤버함수 CreateInstance();

  • CoCreateInstance()호출시에 내부적으로 CoGetClassObject를 호출

    • CoGetClassObject(... ,IID_ICF,&pCF)

      • Registry를 뒤져서 Dll을 로딩한다.
      • DllGetClassObject 
    • pCF->CreateInstance(.....)   (IClassFactory의 첫번째 함수.)

  •  CoCreateInstance() 는 내부적으로 IID_ICF를 호출하기 때문에( HardWire ), IID_ICF2등의 다른 인터페이스를 호출하고 싶을때는

    • CoGetClassObject(...,IID_ICF2,&pCF); / pCF->CI(); / pIX->play(); 처럼 내부 코드를 직접 구현해야 한다.
    • 또한 IClassFactory의 두번째 이상의 함수들(Lock Server)등을 사용하고 싶을때도 CoGetClassObject 로 내부 코드를 직접 사용하여야 한다. 
    • Customized CF 

IDL
  • MIDL

    • 인터페이스 정의 포함 헤더파일 생성(언어에 맞는) 
    • 프록시 , 스텁 dll을 만든다. 
    • 자동화때 사용되는 TypeLibrary를 만든다. 
  • IDL에는 헤더와 본체가 있다.
  • in/out의 개념

    • Mashaling시에 보낼 필요없는 패킷의 낭비를 막고 절약하게 해준다. 
    • Dll에서는 marshaling이 일어나지 않기 때문에 필요없는 기호이다. 
    • [in] Client에서 Server로

      •  ex) [in, size_is(100)]SendMessage(char * arr)
    • [out] Server에서 Client로 
  • 문제가 생기면( 전송했는데 전송받지 못하는 등의) IDL파일을 제일 먼저 확인하는것이 중요하다. 


  • DLL서버의 종료조건

    • Local :    Object == 0  && Lock cnt == 0   (LC는 LCF::LockServer)
    • Out-Of-Process :     종료조건이 만족되면 발생하는 윈도우 메시지에 대하여 코딩해줘야 한다.

      • void CloseExe(void)

        {

        if(g_cObject == 0 && g_cLocks == 0)
                 PostQuitMessage(0);

        }

  • Custom Interface의 프록시/스텁 생성

    • Win32 DDL 유형의 프로젝트를 생성 후 이들 파일을 프로젝트에 추가하여 DLL생성.

      • RPCRT4.Lib가 링크되어 있어야 함 
      • REGISTER_PROXY_DLL 매크로 정의 
      • GetProxyDllInfo함수 export! 
  •  Com Server는 쓰레드 풀을 가지고 있다. 클라이언트가 함수 요청을 하면 윈도우즈 메시지 형태로 패킷이 바뀌어 서버의 Message Loop속으로 들어간다.

    • 메시지 루프에 순서대로 들어간다. 
    • 요청을 동시에 처리하지 않고 Serialising 처리를 해줄 수 있다. -> 기본적(default)으로는 멀티 쓰레딩 환경을 고려하지 않아도 된다. 

  • i,x - 16진수
    i,o - 8진수
    hr,hr  - Hresult 값이 구체적으로 나온다. 


> cd C:\Program Files\Microsoft Visual Studio 8

> vcvars32


  • DllCanUnloadNow - 서버의 소멸 if(o.c == 0 && l.c == 0)

    • Garbage Collecter에 의해 호출.
    • -> CoFreeUnusedLibraries()호출 .
Aggregation  - Binary Composition



  • Type library

    • Language independent header (언어 독립적인 헤더파일) 
    • .TLB 확장자를 갖는 파일
    • Visual C로 Conversion 시켜서 사용하면 된다.( ex).h )
    • DLL이나 EXE파일에 리소스로 포함가능. 
  • TLB 생성  - IDL을 이용. (MIDL로 처리) 
  • .TLB를 .h파일로 변환 - VISUAL C++ 의 #import를 사용(-no_namespave, named_guids)

    • => .TLH파일 생성/ TLI파일 생성.
    • TLH 파일

      • declspec(uuid()) - COM 개체나 인터페이스에 GUID 지정. (ex) 인터페이스 아이디와 인터페이스 GUID를 연결.) 
      • SMARTPTR - 인터페이스를 스마트 포인터로 사용. ex) char*를 CString으로 가리켜 사용하는 것.
    • TLI 파일

      • 함수 호출시 에러가 발생하면(FAILED(_hr)) =>error메시지를  try catch구문에 보내서 발생된 에러를 처리해줘야 한다. 
  • uuidof() - declspec으로 저장된 uuid를 꺼내오는 방법 
  • _com_ptr_t SMARTPTR  - CCI, AddRef, Release, QI 기능을 클래스 안에 감춤으로써 개발자들이 편리하게 인터페이스 포인터를 사용할 수 있게 해준다. 
  • try catch 에러 처리 구문

    • hr코드로 확인하던 에러 구문을 try/catch구문으로 마지막에 한번에 처리할 수 있다. 
  • property - 두개의 함수를 하나의 변수로 사용.

    • __declspec(property(get = GetAddEnd, put = PutAddEnd)) short AddEnd;

      • put으로 쓸때:  AddEnd = 10; 
      • get으로 쓸때:  iVal = AddEnd; 

ATL
  • Active Template Libarary 
  • MFC가 크고 느린 COM개체를 만드는데 반하여 작고 확장성을 가지는 빠른 COM개체를 만든다. 



Automation
  • 정의 : 주로 Script언어가 서버의 개체를 access 하기 위해 사용되는 기법.
  • 특징(제약)

    • 1.Data type의 제한. (사용자정의 Data type을 직접 지원하지 않는다.)
      => VARIANT type의 사용.
    • 2.Header가 없다.
      => Script언어의 Compiler가 interface를 access할수 있는 code를 생성할 수 없다.
      => Idispatch::(표준 인터페이스임)를 사용해서
           Idispatch::Invoke();에 함수 호출 정보를 인자(VARIANT type)로 넣어, 서버에 보내어 Runtime type checking을 해준다.
    • 3.Interface개념을 이해하지 못한다.
      => Script언어는 Interface의 개념을 이해하지 못하므로 MethodProperty, Event로 기능을 내놓아야한다. 

  • 인터페이스가 IDispatch하나를 통해서 invoke()를 거친다. 결국 VisualBasic이 접근하기 편하고 C++에는 좀 불편함이 있다.
    =>Dual Interface( IDispatch에서 상속받은 interface )
  • 프록시/스텁 DLL을 따로 만들 필요 없음. OleAuto32.dll을 사용.
  • 실행 속도가 느림. => Dual Interface(이중인터페이스)로 극복해야 한다...

  • IDispatch

    • GetTypeInfoCount
    • GetTypeInfo 
    • GetIDsOfNames
    • Invoke

      • virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(    
                    /* [in] */ DISPID dispIdMember,
                    /* [in] */ REFIID riid,
                    /* [in] */ LCID lcid,
                    /* [in] */ WORD wFlags,
                    /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,      //VARIANT type의 배열.
                    /* [out] */ VARIANT __RPC_FAR *pVarResult,                     //결과값. 역시 VARIANT type.
                    /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,                 //예외정보.
                    /* [out] */ UINT __RPC_FAR *puArgErr) = 0;                       //에러 매개변수 인덱스.
            };
  • 디스패치 인터페이스

    • Invoke를 통해서 가리키게 되는 함수의 집합. 

  • Late Binding (Run-time)

    • 자동화 컨트롤러가 실행 시간에 자동화 개체의 속성이나 메소드와 결합
    • 실행시간에 동적으로 자동화 개체의 타입 정보를 알 수 있다.
    • 실행에러  E_INVALIDARG에러 발생 
  • Early Binding (Compile time)

    • 컴파일시에 속성과 메소드, 매개변수의 수와 데이터 형을 검사
    • TypeLibrary가 필요하다. 
    • ID 바인딩 (Invoke()만 실행시간에 실행하고 GetIDsOfName()은 미리 컴파일 타임에. )
    • Vtable 바인딩

      • Invoke() 나 GetIDsOfName()등을 사용하지 않고 직접 헤더를 통해서 억세스한다. 

  • HRESULT AddEnd([out, retval] short *pVal);

    • [retval]뒤의 매개 변수를 리턴값으로 해준다. 

  • 자동화 데이타형

    • VARIANT 사용법

      VARIANT v;
      v.vt = UT_I2;      //타입
      v::val=10;

    • 컨트롤러와 서버 사이에 오고 가는 데이터 타입의 제약
      Boolean   /char   /double   /int   /float   /long   /short   /void   /wchar_t   /BSTR
      CURRENCY   /DATE   /HRESULT   /LPWSTR   /LPSTR   /SCODE   /VARIANT   /Idispatch   /IUnknown 
    • 제약에 의한 잇점

      • 마샬링이 단순해짐
      • 데이터 타입에 대해 프로그래밍해야하는 컨트롤러의 부담이 적다.
      • 스크립트언어접근(데이타 타입을 다루기) 쉬워진다. 
  • VARIANT에서 제공하는 데이타 형 
    • BSTR

      •  2개의 필드로 구성
      • 유니코드 (2byte씩 차지.)
        5     h    \0   e    \0   l   \0   l    \0    o    \0    \0    \0 
      •  관련 API -new()등으로 초기화 할수 없다.
        SysAllocString(), SysFreeString()
        SysReAlloctring(),
        SysStringLen() 
    •  SAFEARRAY 데이터형  

      •  typedef struct  tagSAFEARRAYBOUND
            {
            ULONG cElements; // 배열 요소수
            LONG lLbound;  // 배열 하위 바운드
            } SAFEARRAYBOUND;
        // 한계를 벗어나지 않게 해준다.
    • IDispatchImpl 템플릿 클래스

      • 이중 인터페이스 지원
      • IDispatch 인터페이스 메소드의 디폴트 구현 코드 제공


  • Error Reporting - 자동화 컨트롤러에게 오류를 알리는 단계

    •  운영체제로부터 오류 객체를 생성하여 얻는 과정 -> CreateErrorInfo API 함수
    •  오류 객체에 정보를 채우는 과정 -> ICreateErrorInfo 인터페이스 사용
    •  IErrorInfo 인터페이스를 구함
    •  IErrorInfo 포인터를 넘겨, SetErrorInfo() API 호출
    •  SetErrorInfo() : 현재의 스레드에 대한 오류 객체로 세팅, HRESULT 성공 혹은 실패
    • 클라이언트와 서버가 스레드를 공유해야 한다.  

APARTMENT
  • 한 개의 프로세스에 여러개의 Apartment가 있을 수 있다.(MTA는 한 프로세스 안에 최대 하나, STA는 한 프로세스에 복수개 가능)
  • 프로세스의 경계를 넘지는 못한다. 
  • Single Threaded Apartment

    • Message Queue를 통해서 STA에 접근한다. 
    • 개체를 STA안에 집어넣으면 함수가 한번에 하나 씩 호출된다. 
    • 호출되는 쓰레드도 똑같다. 
    • Serialize가 된다.  
    • block된 STA로의 CALLBACK에 의한 DeadLock이 Com에서의 처리로 발생하지 않는다. 
  • Multi Threaded Apartment

    • CoInitialize는 MTA로 생성 
    • RPC tread pool과 개체 사이에 Message Queue가 없다. 
    • 여러개의 함수가 동시에 실행된다. 
    • Serialize가 되지 않는다. 
  • 자기가 만든 객체가 멀티스레드환경에서 깨지지 않을 자신이 없다면 STA를 써야한다. 
  • Apartment할당

    •  CoInitialize (NULL); // STA
    • CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); // STA
    • CoInitializeEx (NULL, COINIT_MULTITHREADED); // MTA
  • Inproc

    • Registry
      ThreadingModel
    • 예)"ThreadingModel"="Apartment"
      ’Free’, ‘Apartment’, ‘Both’
  • Out-of-Proc

    • EXE 서버 스레드 생성 CoInitialize => Apartment하나가 할당. => 생성되는 개체도 같은 Apartment에 존재하게 된다. 
  • Apartment 경계를 넘어서면 Marshalling이 일어난다.   (EXE server)
  • STA-STA간의 통신

    • EXE

      • 마샬링 필요. 
      • 서버 STA는 반드시 메시지 루프를 돌아야한다.
    • DLL - Client thread가 있는곳에 서버 객체가 생성. 
  • MTA-STA간의 통신

    • CI(MTA) - client가 MTA , 서버가 STA

      • 마샬링 필요. 
      • 서버 STA는 반드시 메시지 루프를 돌아야한다. 
  • MTA-MTA간의 통신

    • 다른 프로세스라는것을 전제한다. 한 프로세스에는 MTA가 하나만 존재할 수 있다.