Sdílet prostřednictvím


TN059: Použití převodních maker MBCS/Unicode prostředí MFC

Poznámka

Následující technická poznámka se od prvního zahrnutí do online dokumentace neaktualizovala. V důsledku toho můžou být některé postupy a témata zastaralé nebo nesprávné. Nejnovější informace doporučujeme vyhledat v online indexu dokumentace, které vás zajímá.

Tato poznámka popisuje, jak používat makra pro převod MBCS/Unicode, které jsou definovány v AFXPRIV.H. Tato makra jsou nejužitečnější, pokud vaše aplikace pracuje přímo s rozhraním OLE API nebo z nějakého důvodu, často potřebuje převést mezi Unicode a MBCS.

Přehled

V prostředí MFC 3.x byla použita speciální knihovna DLL (MFCANS32.DLL) k automatickému převodu mezi kódováním Unicode a MBCS při volání rozhraní OLE. Tato knihovna DLL byla téměř průhledná vrstva, která umožňovala psát aplikace OLE, jako by rozhraní a rozhraní OLE byly MBCS, i když jsou vždy Unicode (s výjimkou Macintosh). I když byla tato vrstva pohodlná a umožňovala rychlý přenos aplikací z Win16 do Win32 (MFC, Microsoft Word, Microsoft Excel a VBA, jsou jen některé z aplikací Microsoftu, které tuto technologii používaly), mělo někdy významný výkon. Z tohoto důvodu MFC 4.x nepoužívá tuto knihovnu DLL a místo toho komunikuje přímo s rozhraními Unicode OLE. K tomu musí mfc při volání rozhraní OLE převést na Unicode na MBCS a při implementaci rozhraní OLE je často potřeba převést na MBCS z Unicode. Aby bylo možné tento převod zpracovat efektivně a snadno, bylo vytvořeno několik maker, aby byl tento převod jednodušší.

Jedním z největších překážek při vytváření takové sady maker je přidělení paměti. Vzhledem k tomu, že řetězce nelze převést na místě, musí být přidělena nová paměť pro uložení převedených výsledků. To by mohlo být provedeno s kódem podobným následujícímu:

// 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;

Tento přístup je řadou problémů. Hlavním problémem je, že se jedná o hodně kódu pro psaní, testování a ladění. Něco, co bylo jednoduché volání funkce, je teď mnohem složitější. Kromě toho je při tom velká režie za běhu. Paměť musí být přidělena na haldě a uvolněna při každém převodu. Nakonec by výše uvedený kód musel být vhodný #ifdefs pro buildy Unicode a Macintosh (které nevyžadují provedení tohoto převodu).

Řešením, se kterým jsme přišli, je vytvořit některá makra, která 1) maskují rozdíl mezi různými platformami a 2) použít efektivní schéma přidělování paměti a 3) je snadné vložit do existujícího zdrojového kódu. Tady je příklad jedné z definic:

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

Použití tohoto makra místo výše uvedeného kódu je mnohem jednodušší:

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

V případě potřeby převodu existují další volání, ale použití maker je jednoduché a efektivní.

Implementace každého makra používá funkci _alloca() k přidělení paměti ze zásobníku místo haldy. Přidělování paměti ze zásobníku je mnohem rychlejší než přidělení paměti na haldě a paměť se automaticky uvolní při ukončení funkce. Kromě toho se makra nevolají MultiByteToWideChar (nebo WideCharToMultiByte) vícekrát. To se provádí přidělením trochu větší paměti, než je nutné. Víme, že MBC se převede na maximálně jeden WCHAR a že pro každý WCHAR budeme mít maximálně dva MBC bajty. Přidělením trochu více, než je nutné, ale vždy dost pro zpracování převodu druhého volání druhého volání funkce převodu se zabrání. Volání pomocné funkce AfxA2Whelper snižuje počet nasdílení argumentů, které je třeba provést, aby bylo možné provést převod (výsledkem je menší kód, než kdyby se volal MultiByteToWideChar přímo).

Aby makra měla místo pro uložení dočasné délky, je nutné deklarovat místní proměnnou s názvem _convert, která to dělá v každé funkci, která používá makra převodu. To se provádí vyvoláním makra USES_CONVERSION, jak je znázorněno výše v příkladu.

Existují obecná makra převodu i makra specifická pro OLE. Tyto dvě různé sady maker jsou popsány níže. Všechna makra se nacházejí v AFXPRIV.H.

Obecná makra převodu

Obecná makra převodu tvoří základní mechanismus. Příklad makra a implementace zobrazená v předchozí části A2W je jedním z takových "obecných" maker. Nesouvisí s OLE konkrétně. Sada obecných maker je uvedena níže:

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

Kromě převodů textu existují také makra a pomocné funkce pro převod TEXTMETRIC, DEVMODE, BSTRa OLE přidělené řetězce. Tato makra jsou nad rámec této diskuze – viz AFXPRIV. H pro další informace o těchto makrech.

Makra převodu OLE

Makra převodu OLE jsou navržena speciálně pro zpracování funkcí, které očekávají znaky OLESTR . Pokud prozkoumáte záhlaví OLE, zobrazí se mnoho odkazů na LPCOLESTR a OLECHAR. Tyto typy se používají k odkazování na typ znaků používaných v rozhraních OLE způsobem, který není specifický pro platformu. OLECHAR se mapuje na char platformy Win16 a Macintosh a WCHAR v systému Win32.

Abychom zachovali počet direktiv #ifdef v kódu MFC na minimum, máme podobné makro pro každý převod, který se týká řetězců OLE. Nejčastěji se používají následující makra:

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

Opět existují podobná makra pro provádění textmetrice, DEVMODE, BSTR a OLE přidělených řetězců. Projděte si AFXPRIV. H pro další informace.

Ostatní úvahy

Nepoužívejte makra v těsné smyčce. Například nechcete psát následující typ kódu:

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

}

Výše uvedený kód by mohl vést k přidělování megabajtů paměti v zásobníku v závislosti na tom, co je obsah řetězce lpsz ! Převod řetězce pro každou iteraci smyčky také nějakou dobu trvá. Místo toho přesuňte takové konstantní převody mimo smyčku:

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

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

}

Pokud řetězec není konstantní, zapouzdřte volání metody do funkce. To umožní, aby se vyrovnávací paměť převodu uvolnila pokaždé. Příklad:

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]);

}

Nikdy nevrací výsledek jednoho z maker, pokud návratová hodnota neznamená vytvoření kopie dat před vrácením. Tento kód je například chybný:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

Výše uvedený kód by mohl být opraven změnou návratové hodnoty na hodnotu, která zkopíruje hodnotu:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

Makra se dají snadno použít a snadno vložit do kódu, ale jak můžete zjistit z výše uvedených upozornění, musíte být při jejich použití opatrní.

Viz také

Technické poznámky podle čísel
Technické poznámky podle kategorií