TN059. Использование макросов преобразования MFC из MBCS в ЮникодTN059: Using MFC MBCS/Unicode Conversion Macros

Примечание

Следующее техническое примечание не было обновлено, поскольку сначала оно было включено в электронную документацию.The following technical note has not been updated since it was first included in the online documentation. В результате некоторые процедуры и разделы могут быть устаревшими или неверными.As a result, some procedures and topics might be out of date or incorrect. Для получения последних сведений рекомендуется выполнить поиск интересующей темы в алфавитном указателе документации в Интернете.For the latest information, it is recommended that you search for the topic of interest in the online documentation index.

В этом заметке описывается использование макросов для преобразования MBCS/Unicode, определенных в АФКСПРИВ. H.This note describes how to use the macros for MBCS/Unicode conversion, which are defined in AFXPRIV.H. Эти макросы наиболее полезны, если приложение напрямую работает с OLE API или по какой-либо причине. часто требуется преобразование между Юникодом и MBCS.These macros are most useful if your application deals directly with the OLE API or for some reason, often needs to convert between Unicode and MBCS.

Общие сведенияOverview

В MFC 3. x используется специальная библиотека DLL (MFCANS32.DLL) для автоматического преобразования между Юникодом и MBCS при вызове интерфейсов OLE.In MFC 3.x, a special DLL was used (MFCANS32.DLL) to automatically convert between Unicode and MBCS when OLE interfaces were called. Эта библиотека DLL была почти прозрачным слоем, в котором разрешено написание приложений OLE, как если бы интерфейсы API OLE и интерфейсы были многобайтовой кодировкой, хотя они всегда поддерживают Юникод (за исключением Macintosh).This DLL was an almost transparent layer that allowed OLE applications to be written as if the OLE APIs and interfaces were MBCS, even though they are always Unicode (except on the Macintosh). Хотя этот уровень был удобен и разрешено быстро перенести приложения из Win16 на Win32 (MFC, Microsoft Word, Microsoft Excel и VBA), это лишь часть приложений Майкрософт, исключающих эту технологию), но иногда существенно страдает производительность.While this layer was convenient and allowed applications to be quickly ported from Win16 to Win32 (MFC, Microsoft Word, Microsoft Excel, and VBA, are just some of the Microsoft applications that used this technology), it had a sometimes significant performance hit. По этой причине MFC 4. x не использует эту библиотеку DLL и вместо этого обращается непосредственно к интерфейсам OLE Юникода.For this reason, MFC 4.x does not use this DLL and instead talks directly to the Unicode OLE interfaces. Для этого MFC необходимо преобразовать в Юникод в MBCS при вызове OLE-интерфейса и часто требуется преобразовать в MBCS из Юникода при реализации интерфейса OLE.To do this, MFC needs to convert to Unicode to MBCS when making a call to an OLE interface, and often needs to convert to MBCS from Unicode when implementing an OLE interface. Для эффективного и простого выполнения этого преобразования было создано несколько макросов.In order to handle this efficiently and easily, a number of macros were created to make this conversion easier.

Одной из крупнейших трудностей при создании такого набора макросов является выделение памяти.One of the biggest hurdles of creating such a set of macros is memory allocation. Поскольку строки не могут быть преобразованы на месте, необходимо выделить новую память для хранения преобразованных результатов.Because the strings cannot be converted in place, new memory to hold the converted results must be allocated. Это можно было сделать с помощью кода, аналогичного приведенному ниже.This could have been done with code similar to the following:

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

Этот подход как ряд проблем.This approach as a number of problems. Основная проблема заключается в том, что это много кода для написания, тестирования и отладки.The main problem is that it is a lot of code to write, test, and debug. Нечто, что было простым вызовом функции, теперь гораздо более сложно.Something that was a simple function call, is now much more complex. Кроме того, в этом случае возникают значительные издержки времени выполнения.In addition, there is a significant runtime overhead in doing so. Память должна выделяться в куче и освобождаться каждый раз, когда выполняется преобразование.Memory has to be allocated on the heap and freed each time a conversion is done. Наконец, приведенный выше код должен был быть соответствующим образом #ifdefs добавлен для сборок в Юникоде и Macintosh (что не требует преобразования).Finally, the code above would need to have appropriate #ifdefs added for Unicode and Macintosh builds (which don't require this conversion to take place).

Решение, с которым мы поступили, заключается в создании макросов, которые 1) позволяют маскировать различия между различными платформами и 2) использовать эффективную схему выделения памяти, а 3) легко вставлять в существующий исходный код.The solution we came up with is to create some macros which 1) mask the difference between the various platforms, and 2) use an efficient memory allocation scheme, and 3) are easy to insert into the existing source code. Ниже приведен пример одного из определений:Here is an example of one of the definitions:

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

Использование этого макроса вместо приведенного выше кода значительно упрощает работу:Using this macro instead of the code above and things are much simpler:

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

Существуют дополнительные вызовы, в которых требуется преобразование, но использование макросов является простым и эффективным.There are extra calls where conversion is necessary, but using the macros is simple and effective.

Реализация каждого макроса использует функцию _alloca () для выделения памяти из стека вместо кучи.The implementation of each macro uses the _alloca() function to allocate memory from the stack instead of the heap. Выделение памяти из стека выполняется гораздо быстрее, чем выделение памяти в куче, и память автоматически освобождается при выходе из функции.Allocating memory from the stack is much faster than allocating memory on the heap, and the memory is automatically freed when the function is exited. Кроме того, макросы не вызывают MultiByteToWideChar (или WideCharToMultiByte ) более одного раза.In addition, the macros avoid calling MultiByteToWideChar (or WideCharToMultiByte) more than one time. Для этого выделяется немного больше памяти, чем требуется.This is done by allocating a little bit more memory than is necessary. Мы понимаем, что MBC будет преобразовывать не более чем на один WCHAR , а для каждого типа WCHAR будет иметь не более двух MBC байт.We know that an MBC will convert into at most one WCHAR and that for each WCHAR we will have a maximum of two MBC bytes. Выделяя немного больше, чем требуется, но достаточно всегда для обработки преобразования. второй вызов функции преобразования следует избегать.By allocating a little more than necessary, but always enough to handle the conversion the second call second call to the conversion function is avoided. Вызов вспомогательной функции AfxA2Whelper сокращает количество push-уведомлений аргументов, которые должны быть выполнены для выполнения преобразования (это приводит к уменьшению объема кода, чем при вызове MultiByteToWideChar напрямую).The call to the helper function AfxA2Whelper reduces the number of argument pushes that must be done in order to perform the conversion (this results in smaller code, than if it called MultiByteToWideChar directly).

Чтобы макросы имели место для хранения временной длины, необходимо объявить локальную переменную с именем _convert, которая делает это в каждой функции, которая использует макросы преобразования.In order to for the macros to have space to store the a temporary length, it is necessary to declare a local variable called _convert that does this in each function that uses the conversion macros. Это делается путем вызова макроса USES_CONVERSION, как показано выше в примере.This is done by invoking the USES_CONVERSION macro as seen above in the example.

Существуют как универсальный макрос преобразования, так и специальные макросы для OLE.There are both generic conversion macros and OLE specific macros. Эти два разных набора макросов обсуждаются ниже.These two different macro sets are discussed below. Все макросы находятся в АФКСПРИВ. H.All of the macros reside in AFXPRIV.H.

Универсальные макросы преобразованияGeneric Conversion Macros

Универсальный макрос преобразования формирует базовый механизм.The generic conversion macros form the underlying mechanism. Пример макроса и реализация, приведенные в предыдущем разделе A2W, — это один из таких «универсальных» макросов.The macro example and implementation shown in the previous section, A2W, is one such "generic" macro. Он не связан с OLE специально.It is not related to OLE specifically. Ниже приведен набор универсальных макросов.The set of generic macros is listed below:

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

Помимо преобразования текста, существуют также макросы и вспомогательные функции для преобразования TEXTMETRIC DEVMODE BSTR выделенных строк,, и OLE.Besides doing text conversions, there are also macros and helper functions for converting TEXTMETRIC, DEVMODE, BSTR, and OLE allocated strings. Эти макросы выходят за рамки данного обсуждения — см. статью АФКСПРИВ. H. Дополнительные сведения об этих макросах.These macros are beyond the scope of this discussion - refer to AFXPRIV.H for more information on those macros.

Макросы преобразования OLEOLE Conversion Macros

Макросы преобразования OLE предназначены специально для обработки функций, которые должны Олестр символы.The OLE conversion macros are designed specifically for handling functions that expect OLESTR characters. При изучении заголовков OLE вы увидите множество ссылок на лпколестр и олечар.If you examine the OLE headers, you will see many references to LPCOLESTR and OLECHAR. Эти типы используются для обозначения типа символов, используемых в интерфейсах OLE, так, что не относится к платформе.These types are used to refer to the type of characters used in OLE interfaces in a way that is not specific to the platform. Олечар сопоставляется с char платформами Win16 и Macintosh и WCHAR в Win32.OLECHAR maps to char in Win16 and Macintosh platforms and WCHAR in Win32.

Чтобы число директив #ifdef в коде MFC было минимальным, у нас есть похожий макрос для каждого преобразования, в котором участвуют строки OLE.In order to keep the number of #ifdef directives in the MFC code to a minimum we have a similar macro for each conversion that where OLE strings are involved. Чаще всего используются следующие макросы:The following macros are the most commonly used:

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

Опять же, существуют похожие макросы для выполнения строк ТЕКСТМЕТРИК, DEVMODE, BSTR и OLE.Again, there are similar macros for doing TEXTMETRIC, DEVMODE, BSTR, and OLE allocated strings. См. АФКСПРИВ. H для получения дополнительных сведений.Refer to AFXPRIV.H for more information.

Другие вопросыOther Considerations

Не используйте макросы в строгом цикле.Do not use the macros in a tight loop. Например, вы не хотите писать код следующего вида:For example, you do not want to write the following kind of code:

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

}

Приведенный выше код может привести к выделению мегабайтов памяти в стеке в зависимости от содержимого строки lpsz .The code above could result in allocating megabytes of memory on the stack depending on what the contents of the string lpsz is! Также требуется время для преобразования строки для каждой итерации цикла.It also takes time to convert the string for each iteration of the loop. Вместо этого перенесите такие преобразования констант из цикла:Instead, move such constant conversions out of the loop:

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

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

}

Если строка не является константой, инкапсулирует вызов метода в функцию.If the string is not constant, then encapsulate the method call into a function. Это позволит освободить буфер преобразования каждый раз.This will allow the conversion buffer to be freed each time. Пример:For example:

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

}

Никогда не возвращают результат одного из макросов, если только возвращаемое значение не подразумевает создание копии данных перед возвратом.Never return the result of one of the macros, unless the return value implies making a copy of the data before the return. Например, этот код неисправен:For example, this code is bad:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // bad! returning alloca memory
}

Приведенный выше код можно исправить, изменив возвращаемое значение на что-нибудь, которое копирует значение:The code above could be fixed by changing the return value to something that copies the value:

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

LPTSTR lpszT = OLE2T(lpsz);

    CoMemFree(lpsz);

return lpszT; // CString makes copy
}

Макросы просты в использовании и легко вставляются в код, но, как вы можете узнать из этих предостережений, необходимо соблюдать осторожность при их использовании.The macros are easy to use and easy to insert into your code, but as you can tell from the caveats above, you need to be careful when using them.

См. также разделSee also

Технические примечания по номеруTechnical Notes by Number
Технические примечания по категориямTechnical Notes by Category