Udostępnij za pośrednictwem


TN059: używanie makr konwersji MFC MBCS/Unicode

Uwaga

Następująca uwaga techniczna nie została zaktualizowana, ponieważ została po raz pierwszy uwzględniona w dokumentacji online. W związku z tym niektóre procedury i tematy mogą być nieaktualne lub nieprawidłowe. Aby uzyskać najnowsze informacje, zaleca się wyszukanie interesującego tematu w indeksie dokumentacji online.

W tej notatce opisano sposób używania makr do konwersji MBCS/Unicode, które są zdefiniowane w AFXPRIV.H. Te makra są najbardziej przydatne, jeśli aplikacja zajmuje się bezpośrednio interfejsem API OLE lub z jakiegoś powodu często musi konwertować pliki Unicode i MBCS.

Omówienie

W MFC 3.x użyto specjalnej biblioteki DLL (MFCANS32.DLL) do automatycznej konwersji między standardami Unicode i MBCS podczas wywoływanych interfejsów OLE. Ta biblioteka DLL była niemal przezroczystą warstwą, która umożliwiała pisanie aplikacji OLE tak, jakby interfejsy i interfejsy OLE były MBCS, mimo że są one zawsze Unicode (z wyjątkiem komputerów Macintosh). Chociaż ta warstwa była wygodna i zezwalała na szybkie przenoszenie aplikacji z win16 do Win32 (MFC, Microsoft Word, Microsoft Excel i VBA, to tylko niektóre aplikacje firmy Microsoft, które używały tej technologii), czasami znacznie osiągnęła wydajność. Z tego powodu biblioteka MFC 4.x nie używa tej biblioteki DLL i zamiast tego komunikuje się bezpośrednio z interfejsami OLE Unicode. Aby to zrobić, MFC musi konwertować na Unicode na MBCS podczas wykonywania wywołania interfejsu OLE i często musi konwertować na MBCS z Unicode podczas implementowania interfejsu OLE. Aby umożliwić wydajną i łatwą obsługę tego typu, utworzono wiele makr w celu ułatwienia tej konwersji.

Jedną z największych przeszkód tworzenia takiego zestawu makr jest alokacja pamięci. Ponieważ nie można przekonwertować ciągów na miejsce, należy przydzielić nową pamięć do przechowywania przekonwertowanych wyników. Można to zrobić za pomocą kodu podobnego do następującego:

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

Takie podejście jest szeregiem problemów. Głównym problemem jest to, że jest dużo kodu do pisania, testowania i debugowania. Coś, co było prostym wywołaniem funkcji, jest teraz znacznie bardziej złożone. Ponadto istnieje znaczne obciążenie związane ze środowiskiem uruchomieniowym. Pamięć musi być przydzielana na stercie i zwalniana za każdym razem, gdy jest wykonywana konwersja. Na koniec powyższy kod musi mieć odpowiednie #ifdefs dodanie do kompilacji Unicode i Macintosh (które nie wymagają przeprowadzenia tej konwersji).

Wymyśliliśmy rozwiązanie polega na utworzeniu niektórych makr, które 1) maskują różnicę między różnymi platformami, a 2) używają efektywnego schematu alokacji pamięci i 3) są łatwe do wstawienia do istniejącego kodu źródłowego. Oto przykład jednej z definicji:

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

Użycie tego makra zamiast powyższego kodu i znacznie prostsze są następujące elementy:

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

Istnieją dodatkowe wywołania, w których jest wymagana konwersja, ale użycie makr jest proste i skuteczne.

Implementacja każdego makra używa funkcji _alloca(), aby przydzielić pamięć ze stosu zamiast sterta. Przydzielanie pamięci ze stosu jest znacznie szybsze niż przydzielanie pamięci na stercie, a pamięć jest automatycznie zwalniana po zakończeniu działania funkcji. Ponadto makra unikają wywoływania MultiByteToWideChar (lub WideCharToMultiByte) więcej niż jeden raz. Odbywa się to przez przydzielanie nieco więcej pamięci niż jest to konieczne. Wiemy, że MBC zostanie przekonwertowany na co najwyżej jedną WCHAR i że dla każdego WCHAR będziemy mieli maksymalnie dwa bajty MBC. Przydzielając nieco więcej niż to konieczne, ale zawsze wystarczy, aby obsłużyć konwersję drugie wywołanie drugiego wywołania funkcji konwersji jest unikane. Wywołanie funkcji AfxA2Whelper pomocniczej zmniejsza liczbę wypchnięć argumentów, które należy wykonać w celu przeprowadzenia konwersji (powoduje to zmniejszenie liczby kodu, niż w przypadku bezpośredniego wywołania MultiByteToWideChar ).

Aby makra miały miejsce na przechowywanie tymczasowej długości, należy zadeklarować zmienną lokalną o nazwie _convert, która wykonuje to w każdej funkcji korzystającej z makr konwersji. Jest to wykonywane przez wywołanie makra USES_CONVERSION, jak pokazano powyżej w przykładzie.

Istnieją zarówno makra konwersji ogólnej, jak i makra specyficzne dla ole. Te dwa różne zestawy makr zostały omówione poniżej. Wszystkie makra znajdują się w AFXPRIV.H.

Makra konwersji ogólnej

Makra konwersji ogólnej tworzą podstawowy mechanizm. Przykład makra i implementacja pokazana w poprzedniej sekcji A2W jest jednym z takich "ogólnych" makr. Nie jest to związane z ole specjalnie. Poniżej wymieniono zestaw makr ogólnych:

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

Oprócz wykonywania konwersji tekstu istnieją również makra i funkcje pomocnicze służące do konwertowania TEXTMETRICciągów przydzielonych do obiektów , DEVMODE, BSTRi OLE. Te makra wykraczają poza zakres tej dyskusji — zapoznaj się z tematem AFXPRIV. H, aby uzyskać więcej informacji na temat tych makr.

Makra konwersji OLE

Makra konwersji OLE są przeznaczone specjalnie do obsługi funkcji, które oczekują znaków OLESTR . Jeśli zbadasz nagłówki OLE, zobaczysz wiele odwołań do LPCOLESTR i OLECHAR. Te typy służą do odwoływania się do typu znaków używanych w interfejsach OLE w sposób, który nie jest specyficzny dla platformy. OLECHAR mapuje na char platformy Win16 i Macintosh oraz WCHAR w win32.

Aby zachować minimalną liczbę dyrektyw #ifdef w kodzie MFC, mamy podobne makro dla każdej konwersji, w której są zaangażowane ciągi OLE. Najczęściej używane są następujące makra:

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

Ponownie istnieją podobne makra do wykonywania ciągów TEXTMETRIC, DEVMODE, BSTR i OLE. Zapoznaj się z tematem AFXPRIV. H, aby uzyskać więcej informacji.

Inne zagadnienia

Nie używaj makr w ciasnej pętli. Na przykład nie chcesz pisać następującego rodzaju kodu:

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

}

Powyższy kod może spowodować przydzielanie megabajtów pamięci na stosie w zależności od zawartości ciągu lpsz ! Konwertowanie ciągu dla każdej iteracji pętli zajmuje również trochę czasu. Zamiast tego przenieś takie stałe konwersje z pętli:

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

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

}

Jeśli ciąg nie jest stały, hermetyzuj wywołanie metody do funkcji. Pozwoli to na zwolnienie buforu konwersji za każdym razem. Przykład:

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

}

Nigdy nie zwracaj wyniku jednego z makr, chyba że zwracana wartość oznacza utworzenie kopii danych przed zwróceniem. Na przykład ten kod jest nieprawidłowy:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

Powyższy kod można naprawić, zmieniając wartość zwracaną na wartość, która kopiuje wartość:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

Makra są łatwe w użyciu i łatwe do wstawiania do kodu, ale jak można powiedzieć z powyższych zastrzeżeń, należy zachować ostrożność podczas ich używania.

Zobacz też

Uwagi techniczne według numerów
Uwagi techniczne według kategorii