본문 바로가기

Robotics/Software Tech.

COM(Component Object Model) 개념잡기

1. COM

1.1 COM의 개념

COM(Component Object Model)이란 한마디로 어떤 프로그램이나 시스템을 이루는 컴포넌트들이 상호 통신할 수 있도록 하는 메커니즘이라고 할 수 있다. 여기서 컴포넌트란 .ocx, .dll, .exe를 확장자로 갖는 실행 가능한 바이너리 화일이다. 물론 COM은 이외에도 다음과 같은 특성 및 기능을 갖는다.

  • 언어 독립성(Language Independence)

이 말은 다른 개발 도구(VB, VJ++, VC++, Delphi 등)를 사용하여 작성한 컴포넌트들이 상호 통신할 수 있다는 것을 의미한다. 다시 말해 VC++로 컴포넌트를 만들고, VB나 Delphi 등 COM을 지원하는 툴로 만든 프로그램(이 프로그램을 COM 컴포넌트의 클라이언트를 하자)에서 컴포넌트를 마치 동일한 개발 도구로 생성한 프로그램 모듈처럼 사용할 수 있다는 것을 의미한다. 이 기능은 개발자들에게 많은 편의를 제공한다.

  • Binary Standard

이 말은 VC++로 작성된 COM 컴포넌트를 VB 등에서 사용하기 위해 VC++의 소스가 전혀 필요없다는 것을 의미한다. VB 개발자는 VC++로 작성된 .exe, .ocx, .dll 등과 같은 binary만 있으면 이 컴포넌트를 사용할 수 있다.

  • Version Control

COM에 기반하여 작성한 컴포넌트는 하위 호환성 및 상위 호환성을 제공한다. 현재 사용되고 있는 모듈에 새로운 기능이 요구된다면 COM에서는 쉽게 새로운 기능을 기존의 모듈에 추가할 수 있다. 새로운 기능을 포함하는 모듈은 새로운 기능을 필요로하는 클라이언트들에게는 새로운 기능을 이전 기능을 필요로 하는 클라이언트들에게는 이전 기능을 동시에 제공할 수 있다.

  • Location Transparency

이 말은 컴포넌트가 물리적 위치에 관계없이 다른 컴포넌트 혹은 컴포넌트 클라이언트에 의해 사용될 수 있다는 것을 의미한다. COM 컴포넌트는 In-process, out-of-process, out-of-machine(네트워크를 통한 리포트 머신)에서 실행될 수 있다.

In-process 형태로 수행되는 컴포넌트는 .dll, .ocx 형태로 생성된다. 이 컴포넌트들은 컴포넌트 클라이언트의 메모리 영역으로 로드되어 사용된다.

out-of-process 형태로 수행되는 컴포넌트는 .exe 형태로 생성된다. 이 부류의 컴포넌트는 클라이언트와 동일한 머신에서 수행된다.

out-of-machine 형태로 수행되는 컴포넌트도 .exe 형태로 생성된다. 이 컴포넌트는 클라이언트가 수행되는 머신과 네트워크를 통해 연결된 다른 머신에서 수행된다.

 

간단하게 COM의 개념에 대해 설명했다. 한마디로 COM은 서로 다른 프로그램들이 서로 통신할 수 있도록 하는 Specification이라고 할 수 있다. 이러한 기능 이외에도 COM은 재사용성, 객체 지향 등 많은 특징 및 기능을 가지고 있다.

 

1.2 Interface

인터페이스란 한마디로 COM이 제공하는 기능이라고 할 수 있다. 실제로 인터페이스는 관련있는 함수들의 집합을 정의하는 수단이다. COM 컴포넌트는 하나 이상의 인터페이스를 제공해야한다.

COM은 언어(개발도구)에 독립적이라고 하였다. 이것은 바이너리 화일(.exe, .dll)로 배포되는 COM 컴포넌트가 자신이 제공하는 기능을 COM 객체를 사용하려고 하는 클라이언트 프로그램에게 알려줄 수 있는 방법이 있어야 한다는 것을 의미한다.

이처럼 바이너리로 배포되는 COM 객체가 제공하는 기능을 기술한 것이 인터페이스이다. 인터페이스는 COM 컴포넌트가 무슨("What") 기능을 제공하는지를 알려준다. 하지만 인터페이스를 통해서 COM 컴포넌트가 어떻게("How") COM 컴포넌트의 사용자가 원하는 기능을 제공하는지는 알 수 없다. 사실 인터페이스에 대해 설명한다면 하면 너무도 길고, 지루한 이야기가 될 것이다. 따라서 그런 지루한 이야기는 관심 있는 분들만 찾아서 보시기 바란다.

"Inside COM"이나 Microsoft Web Site의 COM Specification을 보면 인터페이스에 대해 자세히 알 수 있습니다. 만일 제가 시간이 나면 인터페이스에 대한 문제는 다시 자세히 다루기로 하겠습니다.

 

2. 첫번째 COM 예제

첫 예제로 사원에 대한 정보를 네트워크를 통해 주고 받는 COM 컴포넌트와 클라이언트를 작성해 보도록 하자.

2.1 COM 컴포넌트 작성

먼저 VC++ 5.0 이상을 이용하여 COM 객체를 만들어 보자. 다음의 절차를 따른다.

  1. File/New/Project에서 "ATL COM AppWizard"를 선택하고 "Project Name"에 Employee라고 입력한다.
  2. "Server Type"에서 "Executable(EXE)"를 선택한다.
  3. "ClassView"에서 "employee classes"를 오른쪽 마우스 버튼으로 선택하고 "New ATL Object"를 선택한다.
  4. "ATL Object Wizard"에서 "objects/Simple Object"를 선택하고 "Next" 버튼을 클릭한다.
  5. "ATL Object Wizard 등록 정보" 대화상자에서 "Short Name"에 "EmpObj"라고 입력한다. 그러면 "CoClass", "Class", "Interface" 등은 자동으로 채워진다. "Attributes" 탭을 클릭한다.
  6. "확인" 버튼을 클릭한다.
  7. 클래스 뷰를 확장시켜서 "CEmpObj"라는 클래스와 "IEmpObj"라는 인터페이스가 생성된 것을 확인한다.
  8. "IEmpObj" 인터페이스를 오른쪽 마우스 버튼으로 클릭하고 "Add Property"를 클릭한다. 리스트 박스로 나타나는 프로퍼티 타입에는 BSTR을 선택하고, 프로퍼티 이름은 Name이라고 입력한 후 "OK" 버튼을 클릭한다.
  9. CEmpObj에 Name 프로퍼티를 저장할 멤버 변수로 char m_szName[40]을 추가한다.
  10. CEmpObj::get_Name의 구현 부분에 다음 코드를 추가한다.

    // TODO: Add your implementation code here
        USES_CONVERSION;
        *pVal = SysAllocString(A2OLE(m_szName));
        return S_OK;
  11. CEmpObj::set_Name의 구현 부분에 다음 코드를 추가한다.

    // TODO: Add your implementation code here   
        USES_CONVERSION;
        strcpy(m_szName, OLE2A(newVal));
        return S_OK;
  12. 서버 코드가 완성되었다. 빌드 한다.

2.2 COM Client 작성

  1. File/New/Projects에서 MFC AppWizard(exe)를 선택하고 프로젝트 이름은 "EmpClient"로 한다.
  2. MFC AppWizard - Step 1 of 4에서 "Dialog based"로 프로그램 유형을 선택한다.
  3. MFC AppWizard - Step 2 of 4에서 "Automation"을 선택하고, "ActiveX Controls" 선택을 해제한다.
  4. "Finish" 버튼을 클릭한다.
  5. stdafx.h 화일에 다음을 추가한다.

    #import "C:\temp\COMSample\COMObject\employee.tlb" no_namespace named_guids
  6. CEmpClientDlg에 IEmpObjPtr 타입 멤버 변수로 m_pEmp를 추가한다.
  7. CEmpClientDlg::OnInitDialog에 다음 코드를 추가한다.

    HRESULT hr = m_pEmp.CreateInstance(__uuidof(EmpObj));
    if(FAILED(hr))
        AfxMessageBox("객체 생성에 실패했습니다 !!");
  8. COM 객체를 사용할 때 다음과 같은 문법을 사용한다.

    8.1 프로퍼티를 읽을 때
        CString strName = (LPCTSTR)m_pEmp->Name;

    8.2 프로퍼티에 값을 셋팅할 때
        m_pEmp->Name = strName.AllocSysString();

    8.3 객체의 메소드를 호출할 때
        hr = m_pEmp->ComputeSalary(nWorkHour);

    8.4 객체 사용을 마치고 객체를 소멸시킬 때
        m_pEmp = 0;