COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가?
본 게시물은 ‘codeproject.com’에 게시된 글 ‘Introduction to COM - What It Is and How to Use It.’을 번역한 것입니다.
원 게시물은 https://www.codeproject.com/Articles/633/Introduction-to-COM-What-It-Is-and-How-to-Use-It에 게재되어 있습니다. 최대한 원문에 적힌 의도를 반영하고자 하였으나, 우리말로 읽었을 때 보다 자연스럽게 하고자 부득이 어순과 어휘를 조정한 부분도 있음을 양해 바랍니다.
또한 본 게시물에서 언급하고 있는 예제 소스 코드는 Visual C++ 6.0을 기준으로 작성되어 있기 때문에 후속 버전의 Visual Studio(또는 Visual Studio .NET)에서 자동 생성되는 COM 코드와는 다소 차이가 있음을 감안하고 읽으시기 바랍니다.
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (1)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (2)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (3)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (4)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (5)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (6)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (7)
- COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (8) [完]
COM 객체로 작업하기
모든 언어는 객체를 다루기 위한 고유한 방법을 가지고 있습니다. 예를 들어 C++에서 여러분은 객체를 스택에 정적할당할 수도 있고, new
키워드를 사용해 동적할당할 수도 있습니다. COM은 언어 중립적이기 때문에 COM 라이브러리는 자체적인 객체 관리 기능을 제공하고 있습니다. COM 객체와 C++ 객체는 다음과 같이 비교 가능합니다.
《객체를 새로 만들 때》
- C++에서는 new
연산자로 동적할당 하거나, 스택에 정적할당 합니다.
- COM에서는 COM 라이브러리에 있는 API를 호출합니다.
《객체를 삭제할 때》
- C++에서는 delete
연산자로 할당을 해제하거나, 스택에 정적할당된 객체에 대해서는 스코프(scope) 바깥으로 프로그램의 흐름을 이동합니다.
- COM에서 모든 객체는 자신의 레퍼런스 카운트를 가지고 있습니다. 호출자는 객체에게 언제 호출자가 실행되었는지를 반드시 알려주어야 합니다. COM 객체는 레퍼런스 카운트가 0이 되었을 때 스스로를 할당 해제합니다.
이제, 객체를 생성하고 파괴하는 두 단계 사이에 독자 여러분은 실질적으로 이 COM 객체를 사용할 수 있습니다. 독자 여러분이 COM 객체를 생성할 때, 여러분은 COM 라이브러리에게 어떤 인터페이스가 필요한지를 알려주게 됩니다. 또한 독자 여러분은 보통의 C++ 객체처럼 포인터를 사용하여 메소드들을 호출할 수 있습니다.
COM 객체를 생성하기
COM 객체를 생성하고 객체로부터 인터페이스를 얻기 위하여, 독자 여러분은 COM 라이브러리의 API인 CoCreateInstance
를 호출하게 됩니다. CoCreateInstance
의 원형은 다음과 같습니다.
HRESULT CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv);
각 파라미터들의 의미는 다음과 같습니다.
rclsid
- coclass의
CLSID
입니다. 예를 들어 바로가기 아이콘을 만들 수 있는 COM 객체를 생성하기 위하여 여러분은CLSID_ShellLink
를 전달할 수 있습니다. pUnkOuter
- 이 파라미터는 COM 객체들을 결합시킬 때 사용합니다. 여기서 결합은 이미 존재하는 coclass에 새로운 메소드들을 덧붙이는 작업을 의미합니다. 객체 결합을 하지 않을 것이므로 우리는 여기에
NULL
을 전달할 것입니다. dwClsContext
- 우리가 사용하고자 하는 COM 서버의 종류를 지정합니다. 본 글에서 우리는 가장 단순한 형태의 서버인 ‘인 프로세스 DLL(in-process DLL)’만을 사용할 것이기 때문에, 우리는 여기에
CLSCTX_INPROC_SERVER
를 전달합니다. 한가지 주의할 것은, 독자 여러분은 ATL에서는 기본값으로 쓰이는CLSCTX_ALL
속성을 사용해서는 안 된다는 것입니다. 왜냐하면 DCOM이 설치되지 않은 Windows 95 운영체제에서는 실패할 것이기 때문입니다. riid
- 독자 여러분이 얻고자 하는 인터페이스의
IID
입니다. 예를 들어,IShellLink
인터페이스의 포인터를 얻고자 할 때 독자 여러분은IID_IShellLink
를 전달할 수 있습니다. ppv
- 인터페이스 포인터의 주소입니다. COM 라이브러리는 이 파라미터를 통해 요청한 인터페이스를 전달할 것입니다.
독자 여러분이 CoCreateInstance
를 호출할 때, 이 함수는 레지스트리에서 CLSID
를 탐색할 것입니다. 그리고 서버의 위치를 읽을 것이고 메모리로 서버를 적재하여 여러분이 요청한 coclass에 해당하는 인스턴스를 만들 것입니다.
아래 코드는 호출 예제입니다. CLSID_ShellLink
에 해당하는 객체를 생성 및 초기화하고, 그 객체의 IShellLink
인터페이스 포인터를 요청하게 됩니다.
HRESULT hr;
IShellLink * pISL;
hr = CoCreateInstance(CLSID_ShellLink, // coclass의 CLSID
NULL, // 통합과 관련된 것이므로 사용하지 않음
CLSCTX_INPROC_SERVER, // 서버의 종류
IID_IShellLink, // 인터페이스의 IID
(void **)&pISL); // 우리가 얻고자 하는 인터페이스 포인터에 대한 포인터
if (SUCCEEDED(hr)) {
// pISH을 사용한 메소드 호출
} else {
// COM 객체를 생성할 수 없는 경우 hr은 오류 코드를 갖습니다
}
먼저 우리는 CoCreateInstance
로부터 반환되는 값을 보관하기 위한 HRESULT
를 선언하고, 또한 IShellLink
형식의 포인터도 선언하였습니다.
새로운 COM 객체를 생성하기 위해 CoCreateInstance
를 호출합니다. hr
이 성공을 나타내는 값을 갖고 있다면 SUCCEEDED
매크로는 TRUE
값을 반환할 것이고, 그렇지 않을 경우 SUCCEEDED
매크로는 FALSE
값을 반환할 것입니다. 코드가 실패했는지 여부를 판단하기 위하여 FAILED
매크로가 또한 제공됩니다.
COM 객체를 해제하기
앞서 설명한 바와 같이 독자 여러분은 COM 객체를 직접 해제할 수는 없습니다. 대신 여러분은 이 객체의 사용을 끝냈다고 알려주기만 합니다. 모든 COM 객체가 구현하고 있는 IUnknown
인터페이스는 Release
메소드를 가지고 있습니다. 독자 여러분은 COM 객체에 이 메소드를 호출함으로써 더 이상 사용하지 않음을 알려주면 됩니다. 독자 여러분이 일단 Release
를 호출하였다면, COM 객체가 언제든지 메모리에서 사라질 수 있으므로 이후로 절대 인터페이스 포인터를 사용해서는 안됩니다.
만일 여러분의 어플리케이션이 여러가지 다양한 COM 객체를 사용하고 있다면, 인터페이스의 사용을 끝낼 때마다 Release
를 호출하는 것이 매우 중요합니다. 여러분이 인터페이스의 사용을 해제하지 않는다면 COM 객체와 이들의 코드를 가지고 있는 DLL들도 메모리에 계속 남게 되어 여러분의 어플리케이션 실행에 불필요하게 붙어있게 됩니다. 여러분의 어플리케이션이 장시간 실행되고 있을 경우 여러분은 유휴시간동안 CoFreeUnusedLibraries
를 실행할 것이 권장됩니다. 이 API는 더 이상 참조되지 않는 COM 서버를 메모리 적재 해제하여 여러분의 어플리케이션의 메모리 사용량을 줄여줍니다.
앞의 예제에 이어서 Release
를 이렇게 사용하면 됩니다.
// 앞에서 적은 바와 같이 COM 객체를 생성한 후
if (SUCCEEDED(hr)) {
// pISL을 사용하는 메소드 호출
// COM 객체에게 우리가 사용을 끝냈음을 알림
pISL->Release();
}
IUnknown
인터페이스에 대해서는 다음 절에서 충분히 설명하겠습니다.