TN059: MFC MBCS/유니코드 변환 매크로 사용

참고 항목

다음 기술 노트는 온라인 설명서에 먼저 포함되어 있었으므로 업데이트되지 않았습니다. 따라서 일부 절차 및 항목은 만료되거나 올바르지 않을 수 있습니다. 최신 정보를 보려면 온라인 설명서 색인에서 관심 있는 항목을 검색하는 것이 좋습니다.

이 참고에서는 AFXPRIV.H에 정의된 MBCS/유니코드 변환에 매크로를 사용하는 방법을 설명합니다. 이러한 매크로는 애플리케이션이 OLE API를 직접 처리하거나 어떤 이유로 유니코드와 MBCS 간에 변환해야 하는 경우에 가장 유용합니다.

개요

MFC 3.x에서는 OLE 인터페이스가 호출되었을 때 유니코드와 MBCS 간에 자동으로 변환하기 위해 특수 DLL(MFCANS32.DLL)이 사용되었습니다. 이 DLL은 OLE API 및 인터페이스가 항상 유니코드임에도 불구하고(Macintosh 제외) MBCS인 것처럼 OLE 애플리케이션을 작성할 수 있는 거의 투명한 계층이었습니다. 이 계층은 편리했고 Win16에서 Win32(MFC, Microsoft Word, Microsoft Excel 및 VBA)로 신속하게 이식할 수 있는 애플리케이션은 이 기술을 사용한 Microsoft 애플리케이션 중 일부에 불과하지만 경우에 따라 성능이 크게 저하되었습니다. 이러한 이유로 MFC 4.x는 이 DLL을 사용하지 않고 유니코드 OLE 인터페이스와 직접 대화합니다. 이를 위해 MFC는 OLE 인터페이스를 호출할 때 유니코드를 MBCS로 변환해야 하며, OLE 인터페이스를 구현할 때 유니코드에서 MBCS로 변환해야 하는 경우가 많습니다. 이 작업을 효율적이고 쉽게 처리하기 위해 이 변환을 더 쉽게 수행할 수 있도록 여러 매크로가 만들어졌습니다.

이러한 매크로 집합을 만드는 가장 큰 장애물 중 하나는 메모리 할당입니다. 문자열을 현재 위치에서 변환할 수 없으므로 변환된 결과를 저장할 새 메모리를 할당해야 합니다. 이 작업은 다음과 유사한 코드로 수행되었을 수 있습니다.

// we want to convert an MBCS string in lpszA
int nLen = MultiByteToWideChar(CP_ACP,
    0,
    lpszA, -1,
    NULL,
    NULL);

LPWSTR lpszW = new WCHAR[nLen];
MultiByteToWideChar(CP_ACP,
    0,
    lpszA, -1,
    lpszW,
    nLen);

// use it to call OLE here
pI->SomeFunctionThatNeedsUnicode(lpszW);

// free the string
delete[] lpszW;

이 방법은 여러 가지 문제입니다. 기본 문제는 작성, 테스트 및 디버그하는 코드가 많다는 것입니다. 간단한 함수 호출이었던 것이 이제 훨씬 더 복잡해졌습니다. 또한 이렇게 하는 데 상당한 런타임 오버헤드가 있습니다. 메모리는 힙에 할당되고 변환이 완료될 때마다 해제되어야 합니다. 마지막으로 위의 코드는 유니코드 및 Macintosh 빌드에 적절하게 #ifdefs 추가되어야 합니다(이 변환이 수행될 필요가 없음).

우리가 내놓은 해결책은 1) 다양한 플랫폼 간의 차이를 마스킹하고, 2) 효율적인 메모리 할당 체계를 사용하고, 3) 기존 소스 코드에 쉽게 삽입할 수 있는 매크로를 만드는 것입니다. 다음은 정의 중 하나의 예입니다.

#define A2W(lpa) (\
((LPCSTR)lpa == NULL) NULL : (\
    _convert = (strnlen(lpa)+1),\
    AfxA2WHelper((LPWSTR) alloca(_convert*2),
    lpa,
    _convert)\)\)

위의 코드 대신 이 매크로를 사용하는 것이 훨씬 간단합니다.

// use it to call OLE here
USES_CONVERSION;
pI->SomeFunctionThatNeedsUnicode(T2OLE(lpszA));

변환이 필요한 추가 호출이 있지만 매크로를 사용하는 것은 간단하고 효과적입니다.

각 매크로의 구현에서는 _alloca() 함수를 사용하여 힙 대신 스택에서 메모리를 할당합니다. 스택에서 메모리를 할당하는 것은 힙에 메모리를 할당하는 것보다 훨씬 빠르며 함수가 종료되면 메모리가 자동으로 해제됩니다. 또한 매크로는 두 번 이상 호출 MultiByteToWideChar 하지 WideCharToMultiByte않습니다. 이 작업은 필요한 것보다 조금 더 많은 메모리를 할당하여 수행됩니다. MBC는 최대 하나의 WCHAR로 변환되며 각 WCHAR에 대해 최대 2개의 MBC 바이트를 갖게 됩니다. 필요한 것보다 조금 더 할당하지만 변환을 처리할 수 있을 만큼 충분히 할당하면 변환 함수에 대한 두 번째 호출 두 번째 호출을 방지할 수 있습니다. 도우미 함수 AfxA2Whelper 를 호출하면 변환을 수행하기 위해 수행해야 하는 인수 푸시 수가 줄어듭니다(이렇게 하면 직접 호출 MultiByteToWideChar 하는 경우보다 코드가 작아집니다).

매크로에 임시 길이를 저장할 공간이 있도록 하려면 변환 매크로를 사용하는 각 함수에서 이 작업을 수행하는 _convert이라는 지역 변수를 선언해야 합니다. 이 작업은 위의 예제와 같이 USES_CONVERSION 매크로를 호출하여 수행됩니다.

제네릭 변환 매크로와 OLE 관련 매크로가 모두 있습니다. 이러한 두 개의 서로 다른 매크로 집합은 아래에 설명되어 있습니다. 모든 매크로는 AFXPRIV.H에 있습니다.

제네릭 변환 매크로

제네릭 변환 매크로는 기본 메커니즘을 형성합니다. 이전 섹션 A2W에 표시된 매크로 예제 및 구현은 이러한 "제네릭" 매크로 중 하나입니다. 특히 OLE와는 관련이 없습니다. 제네릭 매크로 집합은 다음과 같습니다.

A2CW      (LPCSTR) -> (LPCWSTR)
A2W      (LPCSTR) -> (LPWSTR)
W2CA      (LPCWSTR) -> (LPCSTR)
W2A      (LPCWSTR) -> (LPSTR)

텍스트 변환 외에도 변환, 및 OLE 할당 문자열을 변환하기 TEXTMETRICDEVMODEBSTR위한 매크로 및 도우미 함수도 있습니다. 이러한 매크로는 이 설명의 범위를 벗어납니다. AFXPRIV를 참조하세요. 해당 매크로에 대한 자세한 내용은 H입니다.

OLE 변환 매크로

OLE 변환 매크로는 OLESTR 문자를 예상하는 함수를 처리하기 위해 특별히 설계되었습니다. OLE 헤더를 검사하면 LPCOLESTROLECHAR대한 많은 참조가 표시됩니다. 이러한 형식은 플랫폼과 관련이 없는 방식으로 OLE 인터페이스에서 사용되는 문자 형식을 참조하는 데 사용됩니다. OLECHAR는 Win16 및 Macintosh 플랫폼 및 Win32의 WCHAR에 매핑 char 됩니다.

MFC 코드의 #ifdef 지시문 수를 최소한으로 유지하기 위해 OLE 문자열이 관련된 각 변환에 대해 유사한 매크로가 있습니다. 가장 일반적으로 사용되는 매크로는 다음과 같습니다.

T2COLE   (LPCTSTR) -> (LPCOLESTR)
T2OLE   (LPCTSTR) -> (LPOLESTR)
OLE2CT   (LPCOLESTR) -> (LPCTSTR)
OLE2T   (LPCOLESTR) -> (LPCSTR)

TEXTMETRIC, DEVMODE, BSTR 및 OLE 할당 문자열을 수행하기 위한 유사한 매크로도 있습니다. AFXPRIV를 참조하세요. 자세한 내용은 H.

기타 고려 사항

꽉 루프에서 매크로를 사용하지 마십시오. 예를 들어 다음과 같은 종류의 코드를 작성하지 않으려는 경우

void BadIterateCode(LPCTSTR lpsz)
{
    USES_CONVERSION;
    for (int ii = 0; ii <10000; ii++)
    pI->SomeMethod(ii, T2COLE(lpsz));

}

위의 코드는 문자열 lpsz 의 내용에 따라 스택에 메가바이트 메모리를 할당할 수 있습니다. 또한 루프의 각 반복에 대한 문자열을 변환하는 데 시간이 걸립니다. 대신 이러한 상수 변환을 루프 밖으로 이동합니다.

void MuchBetterIterateCode(LPCTSTR lpsz)
{
    USES_CONVERSION;
    LPCOLESTR lpszT = T2COLE(lpsz);

    for (int ii = 0; ii <10000; ii++)
    pI->SomeMethod(ii, lpszT);

}

문자열이 상수가 아니면 메서드 호출을 함수로 캡슐화합니다. 이렇게 하면 변환 버퍼가 매번 해제될 수 있습니다. 예시:

void CallSomeMethod(int ii, LPCTSTR lpsz)
{
    USES_CONVERSION;
    pI->SomeMethod(ii, T2COLE(lpsz));

}

void MuchBetterIterateCode2(LPCTSTR* lpszArray)
{
    for (int ii = 0; ii <10000; ii++)
    CallSomeMethod(ii, lpszArray[ii]);

}

반환 값이 반환 전에 데이터의 복사본을 만드는 것을 의미하는 경우가 아니면 매크로 중 하나의 결과를 반환하지 않습니다. 예를 들어 이 코드는 잘못되었습니다.

LPTSTR BadConvert(ISomeInterface* pI)
{
    USES_CONVERSION;
    LPOLESTR lpsz = NULL;
    pI->GetFileName(&lpsz);

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

위의 코드는 반환 값을 값을 복사하는 값으로 변경하여 수정할 수 있습니다.

CString BetterConvert(ISomeInterface* pI)
{
    USES_CONVERSION;
    LPOLESTR lpsz = NULL;
    pI->GetFileName(&lpsz);

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

매크로는 사용하기 쉽고 코드에 삽입하기 쉽지만 위의 주의 사항에서 알 수 있듯이 매크로를 사용할 때는 주의해야 합니다.

참고 항목

번호별 기술 참고 사항
범주별 기술 참고 사항