^(코딩캣)^ = @"코딩"하는 고양이;

COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (6)

API/COM
2020. 10. 4. 13:20

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 코드와는 다소 차이가 있음을 감안하고 읽으시기 바랍니다.

  1. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (1)
  2. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (2)
  3. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (3)
  4. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (4)
  5. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (5)
  6. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (6)
  7. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (7)
  8. COM의 소개(파트 1) – COM이 무엇이며, 어떻게 사용하는가? (8) [完]

 

주의 깊게 보세요 – 문자열 취급

COM 코드에서 문자열을 다루는 방법을 설명하기 위하여 잠시 다른 이야기를 해볼까 합니다. 독자 여러분이 Unicode와 ANSI 문자열이 어떻게 작동되는지에 대해 잘 알고 있고, 이 두 문자열 사이에서 어떻게 변환해야 하는지도 잘 알고 있다면 이 절을 건너뛰어도 됩니다. 그렇지 않은 경우 이 절을 주의 깊게 읽어보시기 바랍니다.

COM 메소드가 문자열을 반환할 때, 그 문자열은 유니코드로 되어있을 것입니다(물론 모든 메소드가 COM 기술 사항을 그대로 만족하면서 작성된다면 말입니다). 유니코드는 아스키코드처럼 문자를 인코딩하는 스키마 중 하나로서, 대부분의 문자는 2바이트 길이를 갖습니다(역자: 원문에는 모든 문자라 되어있지만, 자주 쓰이지 않는 한자(벽자)라든가 최근 도입되는 이모지 문자처럼 UTF-16으로 인코드할 때 1글자가 2바이트 이상인 문자도 있습니다). 독자 여러분이 문자열을 보다 처리하기 용이한 형태로 얻기 위해서 COM 메소드가 반환하는 문자열을 TCHAR 형 문자열로 변환하여야 합니다.

TCHAR라든가 _t로 시작하는 함수들(예를 들어 _tcscpy)은 컴파일러 설정에 따라 여러분이 하나의 소스코드로 유니코드 문자열과 ANSI 문자열 모두를 다룰 수 있도록 디자인된 것입니다. 대부분의 경우 여러분은 ANSI 문자열과 ANSI형 Windows API를 사용하는 코드를 작성하게 될 것입니다(역자: Windows NT, 2000 및 XP 이후로는 굳이 컴파일러의 설정을 바꾸지 않는 이상 TCHAR 형이 WCHAR 형에 맞추어져 있으므로 유니코드 문자열이 기본입니다. 그러나 printf 등의 C 함수들을 사용할 경우를 대비하여 유니코드 문자열을 ANSI 문자열로 변환하는 방법을 알아둘 필요는 있습니다.). 때문에 이 글의 대부분에서, 필자는 간결함을 위해 TCHAR 대신 char을 사용할 것입니다. 여러분은 다른 누군가가 작성한 코드를 마주하였을 때를 대비하여 TCHAR 타입에 대하여 확실하게 숙지하시기 바랍니다.

여러분이 COM 메소드로부터 유니코드 문자열을 전달받았을 때 여러분은 이를 다음 몇 가지 방법으로 char 형 문자열로 변환할 수 있습니다.

1. WideCharToMultiByte Windows API를 호출하기

2. CRT 함수인 wcstombs 호출하기

3. MFC에 한하여 CString 생성자 또는 할당 연산자 사용하기

4. ATL 문자열 변환 매크로 사용하기

 

WideCharToMultiByte

WideCharToMultiByte Windows API를 사용하여 여러분은 유니코드 문자열을 ANSI 문자열로 변환할 수 있습니다. 원형은 다음과 같습니다.

int WideCharToMultiByte(
    UINT CodePage,
    DWORD dwFlags,
    LPCWSTR lpWideCharStr,
    int cchWideChar,
    LPSTR lpMultiByteStr,
    int cbMultiByte,
    LPCSTR lpDefaultChar,
    LPBOOL lpUsedDefaultChar);

각 파라미터의 의미는 다음과 같습니다.

CodePage
유니코드 문자열을 ANSI 문자열로 변환할 때 대상이 되는 코드페이지입니다. 운영체제에서 현재설정된 ANSI 코드페이지로 변환하고자 할 때 여러분은 CP_ACP를 전달하면 됩니다(역자: 기본 로마자가 1바이트이고, 한글/한자/특수기호가 2바이트인 일반적인 한국어 ANSI 문자열은 CP 949입니다). 코드페이지는 256개의 문자 집합으로 되어 있습니다. 0부터 127번 문자는 항상 표준 아스키코드 문자에 대응합니다. 128번부터 255번 문자는 코드페이지마다 달라지며, 그래픽문자나 발음기호를 포함하는 로마자가 포함될 수 있습니다. 각 언어 또는 지역은 각자의 코드페이지를 갖습니다. 그러므로 강세표시된 문자를 올바르게 표시하기 위해서는 정확한 코드페이지를 지정하고 사용할 필요가 있습니다(역자: 한국어의 경우 CP 949를 맞추어 주어야 글자가 깨지지 않습니다).
dwFlags
조합된 유니코드 문자를 어떻게 다룰 것인지에 대한 방법을 지정합니다. 여기서 조합된 문자란, 기본 로마자에 강세표시나 발음기호 문자 등이 붙는 경우입니다. 예를 들어 è라는 문자에 대해 보겠습니다. 지정된 코드페이지에 이러한 문자가 정의되어 있다면, 아무 문제 없이 그대로 코드값만 바뀝니다. 그러나 해당 코드페이지에 이 문자가 정의되어 있지 않은 경우 Windows 운영체제는 이 문자를 다른 무언가로 바꾸어 나타낼 필요가 있습니다.
WC_COMPOSITECHECK 옵션을 전달하면, 이 API는 지정된 코드페이지에 그러한 문자가 정의되어 있는지를 사전에 검사할 수 있습니다. WC_SEPCHARS 옵션을 전달하면 Windows 운영체제는 위와 같은 글자를 기본 로마자(e)와 강세 표시(`)라는 2개의 문자로 쪼개어 표현할 수 있습니다. WC_DISCARDNS 옵션을 전달하면 Windows 운영체제는 위와 같은 문자에 대해 강세표시나 발음기호를 제거하고 기본 문자인 e로만 변환합니다. WC_DEFAULTCHAR 옵션을 전달하면 Windows 운영체제는 후술할 lpDefaultChar 파라미터로 전달된 기본 문자로 치환합니다. 이 파라미터의 기본값은 WC_SEPCHARS입니다.
lpWideCharStr
특정 코드페이지의 ANSI 문자열로 변환할 원본 유니코드 문자열입니다.
cchWideChar
유니코드 문자열인 lpWideCharStr의 길이입니다. 여러분은 대개 -1을 전달하게 될 것입니다. 이 경우 지정된 문자열이 NULL 문자로 끝난다는 것을 의미하고 API가 알아서 문자열의 길이를 측정합니다.
lpMultiByteStr
ANSI 문자열로 변환된 결과가 보관될 버퍼입니다.
cbMultiByte
lpMultiByteStr 버퍼의 크기입니다. 단위는 바이트입니다.
lpDefaultChar
이는 선택적으로 전달되는 파라미터입니다. dwFlags 매개변수가 WC_COMPOSITECHECK | WC_DEFAULTCHAR를 포함하고 있을 때, 특정 코드페이지에서 정의되지 않았던 문자는 이 매개변수에서 제시한 문자로 치환됩니다. 여러분이 이 매개변수에 NULL을 전달하면 운영체제에서 기본으로 정의된 문자로 치환될 것인데, 대체로 물음표(?) 문자가 나타날 것입니다.
lpUsedDefaultChar
이는 선택적으로 전달되는 파라미터입니다. BOOL 형 변수에 대한 포인터로서, ANSI 문자열로 변환할 때 호환되지 않는 문자가 있어서 앞서 설명한 lpDefaultChar가 사용되었다면 여기로 전달한 BOOL 형 변수의 값은 TRUE로 바뀔 것입니다. 이 정보가 필요없다면 NULL을 전달해도 됩니다.

매우 길고도 많은 매개변수 설명이었습니다. MSDN 문서에는 이보다 더 복잡한 설명들이 포함되어 있습니다. 이제 이 API의 사용 예를 보겠습니다.

// wszSomeString으로 유니코드 문자열을 이미 가지고 있다고 가정
wchar_t wszSomeString[MAX_PATH];
char szANSIString[MAX_PATH];

WideCharToMultiByte(CP_ACP, // 운영체제에 현재 설정된 ANSI 코드 페이지
    WC_COMPOSITECHECK, // 강세표시된 문자가 있는지 체크함
    wszSomeString, // 원본 유니코드 문자열
    -1, // wszSomeString이 NULL 문자로 끝나는 문자열임
    szANSIString, // char형 버퍼
    sizeof(szANSIString), // 버퍼의 크기(바이트)
    NULL, // 변환 실패 시 치환할 기본 문자 없음
    NULL); // 변환 실패한 문자 여부를 가져올 필요 없음

위와 같이 호출하면 szANSIString은 원본 유니코드 문자열에 대한 ANSI 버전의 사본을 갖게 됩니다.

 

wcstombs

CRT 함수인 wcstombs는 호출이 간단하지만 결국 WideCharToMultiByte를 호출하게 됩니다. 그러므로 결과는 동일합니다. wcstombs의 원형은 다음과 같습니다.

size_t wcstombs(char * mbstr, const wchar_t * wcstr, size_t count);

각 파라미터는 다음과 같습니다.

mbstr
ANSI 문자열을 갖게 되는 char 형 버퍼입니다.
wcstr
변환하고자 하는 원본 유니코드 문자열입니다.
count
mbstr 버퍼의 크기입니다. 단위는 바이트입니다.

wcstombs는 내부적으로 WideCharToMultiByte를 호출할 때 WC_COMPOSITECHECK | WC_SEPCHARS 옵션을 사용합니다. 앞의 예제를 다시 사용하면, 독자 여러분은 유니코드 문자열을 다음과 같이 변환할 수 있습니다.

wcstombs(szANSIString, wszSomeString, sizeof(szANSIString));

 

CString

MFC CString 클래스는 유니코드 문자열을 받아들일 수 있는 생성자와 할당 연산자를 포함하고 있습니다. 때문에 독자 여러분은 CString이 알아서 변환을 하도록 그냥 두면 됩니다. 예를 들면 다음과 같습니다.

CString str1(wscSomeString); // 생성자를 사용하여 변환
CString str2 = wszSomeString; // 대입 연산자를 사용하여 변환

 

ATL 매크로

ATL은 문자열 변환을 위한 유용한 매크로 집합을 포함하고 있습니다. 유니코드 문자열을 ANSI 문자열로 변환하기 위하여 W2A(wide to ANSI) 매크로를 사용합니다. 보다 더 정확하게 말하자면, 여러분은 OLE2A를 사용해야 합니다. 여기서 OLE는 COM이나 OLE에서 전달된 문자열을 의미합니다. 어쨌든, 이들 매크로를 사용하는 방법은 다음과 같습니다.

#include <atlconv.h>

// wszSomeString을 이미 가지고 있다고 가정
wchar_t wszSomeString[MAX_PATH];
char szANSIString[MAX_PATH];

USES_CONVERSION; // 매크로에서 사용되는 지역변수들을 선언
lstrcpy(szANSIString, OLE2A(wszSomeString));

OLE2A 매크로는 변환된 문자열에 대한 포인터를 반환합니다. 그런데 이 변환된 문자열은 임시변수인 스택 위치에 있는 버퍼에 보관되어 있습니다. 그러므로 우리는 lstrcpy를 사용하여 복사본을 만들어야 합니다. 또 다른 매크로로서 독자 여러분이 아셔야 할 것은 W2T(유니코드 문자열을 TCHAR 형 문자열로 변환) 및 W2CT(유니코드 문자열을 const TCHAR 형 문자열로 변환)가 있습니다.

위 예제코드에서 설명했던 OLE2A와 마찬가지로 OLE2CA(유니코드 문자열을 const char 형 문자열로 변환) 매크로도 있습니다. 위 예제코드의 경우 사실 OLE2CA를 사용하는 것이 더 알맞습니다. 왜냐하면 lstrcpy의 두 번째 파라미터가 const char * 형 문자열을 받기 때문입니다. 그러나 필자는 여러분에게 한번에 너무 과도한 정보를 전달하고 싶지는 않습니다.

 

유니코드로 고정하기

앞서 설명한 것과는 반대로, 여러분은 문자열에 대한 너무 복잡한 처리과정을 피하고자 COM으로부터 받은 문자열을 유니코드 문자열 그대로 보존할 수도 있습니다. (역자: 원 글이 작성된 시기가 2000년 ~ 2001년 사이임을 감안하여야 합니다. 이 당시 일반 사용자 운영체제는 ANSI 문자열이 기본인 Windows 9x가 주류였고, 원 글 작성자의 모국어가 영어인 만큼 1문자당 1바이트면 충분했습니다. 현재에는 윈도우 운영체제 자체가 유니코드에 기반한 다국어 환경을 기본으로 지원하기 때문에 TCHAR = WCHAR = wchar_t = 2 바이트가 시스템 기본 문자형입니다.) 특히 여러분이 콘솔 어플리케이션을 작성할 때 여러분은 std::wcout라는 전역 객체를 사용하여 유니코드 문자열을 출력할 수 있습니다. 사용 예는 다음과 같습니다.

std::wcout << wszSomeString;

std::wcout은 모든 문자열이 유니코드 문자열일 것이라 간주하고 있음을 숙지하시기 바랍니다. 여러분이 보통의 문자열(ANSI 문자열)을 출력하고자 할 때는 std::cout으로 출력합니다. 문자열 리터럴(문자열 상수)을 출력하고자 할 때 문자열 상수 앞에 접두어 L을 붙임으로써 유니코드 문자열임을 표시합니다. 예를 들어 다음과 같습니다.

std::wcout << L"The Oracle says…" << std::endl << wszOracleResponse;

독자 여러분이 유니코드 문자열을 그대로 쓰고자 할 때 몇 가지 제한 사항이 있습니다.

1. 유니코드에 대해 CRT 함수를 사용하고자 할 때 wcslen과 같이 유니코드 버전인 wcsXXX 함수를 사용해야 합니다.

2. 몇몇 예외적인 경우로서, 독자 여러분은 Windows 9x 환경에서 유니코드 문자열을 Windows API의 파라미터로 전달할 수 없습니다. 소스코드 변경 없이 9x와 NT 커널 모두에서 사용하고자 할 때, MSDN에 나온대로 TCHAR 문자형을 사용해야 합니다.

카테고리 “API/COM”
more...