본문 바로가기
Programming/Tips(C++,C#)

Chapter 05. Out-of-Process server COM component

by 곰네Zip 2014. 11. 10.

5.1 마샬링의 이해

  5.1.1 아웃-오브-프로세스 서버 COM컴포넌트 구현시 고려사항

    - 인 프로세스 서버에서는 COM컴포넌트가 가리키는 주소 값은 클라이언트와 같은 영역 안에 존재한다. (인-프로세스 서버버 컴포넌트는 클라이언트에서 해당 DLL을 불러온다.) 그러나 아웃-오브-프로세스 서버는 다른 프로세스 메모리 영역에 위치한다. 즉, COM컴포넌트에서 리턴해 준 인터페이스 주소를 클라이언트에서는 사용할 수 없다.

 

   5.1.2 표준 마샬링 과정

    - COM은 IPC(Inter-Process Communication. 프로세스간 통신)을 위해 LPC(Local Procedure Call)과 RPC(Remote Procedure Call) 두가지를 사용한다. LPC는 같은 시스템 상에서 실행되는 아웃-오브-프로세스 서버애 접근할 때, RPC는 다른 시스템의 아웃-오브-프로세스 서버에 접근할 때 사용된다.

    - 서로 다른 프로세스 사이에 통신을 하면서, 다른 프로세스에서 데이터를 읽을 수 있도록 표준 형식으로 변환하는 과정이 마샬링이다. (반대로 표준 형식을 프로세스에서 읽는 과정은 언마샬링)

     i) proxy : 클라이언트단 동작. 클라이언트에서 COM서버로 요청을 보낼 때, 데이터를 마샬링하여 전송하는 역할

     ii) stub : 서버단 동작, 클라이언트에서 요청이 들어오면 해당 데이터를 언마샬링 하고, COM컴포넌트의 객체를 호출하고 그 결과를 다시 프록시에 전달하여 준다.

 

  5.1.3 커스텀 인터페이스의 프록시/스텁 생성

   표준 COM인터페이스에 대한 마샬링 코드는 OS레벨에서 지원되지만, 커스텀 COM인터페이스에 대해서는 별도의 프록시/스텁 dll을 같이 제공해야 한다. 

    midl컴파일러로 idl을 컴파일 하면 프록시/스텁 코드를 얻을 수 있다. (기본 64비트라 32비트용은 /env win32옵션) 

 midl /env win32 /Oic /h hello_h.h hello.idl

     > 만약 vc6으로 수행하면 VS폴더\vc98\bin을 path에 추가해 주고, .net부터는 닷넷 프롬프트를 이용하자. 위 명령을 입력하고 나면 hello_h.h, hello_i.c hello_p.c dlldata.c 파일이 생성된다. 이 파일들을 프로젝트에 추가하고 빌드하자.

 

  5.1.4 효율적인 마샬링을 위한 IDL 애트리뷰트.

    attribute는 메소드의 인스가 마샬링되는데 필요한 정보를 제공하여 실행되는 코드를 최적화 할 수 있다. 여기서는 마샬링에 필요한 attribute에 대해서 간단히 기입

     - 포인터

        포인터가 매개변수로 넘어갈 때, 포인터가 가리키는 메모리의 데이터를 복사하여 넘기느라 효율성이 떨어진다. 이를 위해 다음과 같은 attribute를 제공한다.

      > idl에서 attribute리스트에 pointer_default(속성값)를 지정하여 두면, 포인터의 기본 attribute지정 가능

 

     - 배열

      배열을 어떻게 넘겨줄 것인가에 대한 정의

     

     - 사용자 정의 데이터타입.

         idl에서는 구조체같은 사용자 정의 데이터 타입도 사용할 수 있다.

 

     - 인터페이스 포인터

         iid_is attribute를 사용한다. (IUnknown과 함께 사용됨) 

 HRESULT GetInterface([in] const IID& iid, [out, iid_is(iid)] IUnknown** ppv);

 

5.2 아웃-오브-프로세스 서버 COM컴포넌트 구현

  - 인-프로세스 서버에서는 Dll에서 함수를 노출시켜 동작하였으나, exe파일에서는 함수 노출이 안됨.

  5.2.1 WinMain함수

    - 아웃-오브-프로세스 서버는 자신의 프로세스 영역에서 실행되므로 DllMain이 아닌 WinMain에서 시작.

      COM라이브러리 초기화/해제, 메시지루프로 구성된다.

 

  5.2.2 CoRegisterClassObject와 CoRevokeClassObject함수

    - 인-프로세스 서버의 경우 CoCreateInstance 호출시, DllGetClassObject의 호출로, 로드되는 때를 알 수 있다. 그러나 아웃-오브-프로세스 서버는 익스포트 함수를 제공하지 않으므로 다른 접근이 필요. 클라이언트에서 CoCreateIntanceEx 요청에 의해 COM라이브러리가 아웃-오브-프로세스 서버 COM컴포넌트의 경로명에 '-Embedding' 매개변수를 추가해서 넘겨줌. 이 매개변수를 체크하여 인스턴스가 생성될 COM객체에 매핑되는 클래스 팩토리 객체를 등록해 주어야 한다.. 서버 종료시에는 COM객체를 해제하여야 함. EXE객체에 대해서 수행됨.

       > CoRegisterClassObject함수를 사용하여 클래스 팩토리를 등록하여 준다. 이과정은 RegisterClass와 비슷하다.

        CoRegisterClassObject함수의 원형 

 STDAPI CoRegisterClassObject(

     REFCLSID rclsid, //등록될 클래스 팩토리 컴포넌트의 클래스ID

     IUnknown* pUnk, //클래스팩토리의 IUnknown 인터페이스 포인터

     DWORD dwClsContext, // 클래스팩토리 실행 컨텍스트

     DWORD flags,   //클래스 팩토리가 생성할 수 있는 컴포넌트의 수

     LPDWORD lpdwRegister // 리턴값 포인터

}

      > 하나의 인스턴스만 서비스 할 경우 CLSCTX_LOCAL_SERVER와 REGCLS_SINGLEUSE를 사용하고, 여러개 사용할 경우 CLSCTX_LOCAL_SERVER와 REGCLS_MULTIPLEUSE를 사용한다. 만약, 여러 COM컴포넌트를 로드하는 서버가, 자신이 로드한 COM컴포넌트를 사용해야 할 일이 생길 때, 아웃-오브-프로세스가 아닌 인-프로세스로 로딩하는것이 유리하다. 이럴 때는 CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER플래그로 결합하자.

      > COM객체를 다 사용하였으면, Factory를 해제해야 한다.

 

  5.2.3 프로세스 종료

     - 인-프로세스 서버의 경우 수동적으로 메모리에서 언로드 된다. (CoFreeUnusedLibraries에서 해제해줌). 그러나 아웃-오브-프로세스 서버의 경우 스스로 프로세스를 종료해야 한다. 객체 참조 외에 lock카운트도 같이 관리해야 함. 클래스팩토리의 LockServer에서 lock카운트 변수를 관리한다.

 

   5.2.4 레지스트리 등록 및 해제.

     - 인-프로세스에서는 DllRegisterServer나 DllUnregisterServer가 호출되어 온다. 하지만, 아웃-오브-프로세스 서버에서는 익스포트 된 함수가 없다. 역시 WinMain의 매개변수의 값을 확인하여 처리한다. (레지스트리 등록은 인-프로세스 서버와 동일하다.)

     위와 같이 빌드 후, -regserver / -unregserver옵션을 주어 실행 했을 때, 레지스트리 등록 여부를 확인하자

 

* 아웃-오브-프로세스 서버의 경우 인-프로세스 서버에서와 차이는 인-프로세스에서 DllRegisterServer, DllUnregisterServer, DllGetClassObject, DllCanUnloadNow등의 함수를 윈도우 메시지 루프에서 처리해준다.

 

반응형

댓글