Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Настройки Unicode и Multi-Byte в MFC-проекте. Или почему не компилируется программа? 
:(
    Опции темы
zkv
Дата 24.9.2007, 00:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Участник Клуба
Сообщений: 2133
Регистрация: 23.7.2006
Где: Санкт-Петербург

Репутация: 16
Всего: 92



Почему не компилируется программа?

Пожалуй самый часто задаваемый вопрос на MFC-форуме выглядит примерно так:

Цитата

У меня в строке:
SetWindowText( "Text" );

компилятор выдает ошибку:
error C2664: 'CWnd::SetWindowTextW' : cannot convert parameter 1 from 'const char [5]' to 'LPCTSTR'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

Что делать?


Дело в том, что у вас в настройках проекта установлена опция "Use Unicode Character Set", а вы пытаетесь использовать неюникодную строку: "Text"

Существуют следующие варианты решения:
1. Отключить юникод в проекте. Самый простой, но идеологически неправильный путь. Когда-нибудь может выйти боком.
2. Использовать юникодные строки. Правильный путь, но следующий лучше. smile
3. Сделать код в проекте независимым от настроек юникода. Самый верный путь.


В чем отличия Unicode Multi-Byte?

простыми словами: UniCode - на символ 2 байта, в MultiByte - 1 байт. Как известно в 1 байте 8 бит. Каждый бит может быть либо 0 либо 1 по определению. И это двоичное исчисление. Следовательно, ты можешь из 8 бит получить 2 в 8 степени комбинаций, то есть 256. А в двух байтах комбинаций 65536. Соответственно любимый всеми албанский язык может не поместиться в 256-ти комбинациях вместе с латинским-английским. А что говорить об иероглифах? Вот и сделали 2 байта - должно хватать всем. Сам понимаешь, что из одного байта ты с большей вероятностью перейдешь без потери информативности в два байта, чем обратно. Следовательно, для того, чтобы программы твои могли ходить по всему миру - работай в Unicode (надо то всего лишь галочку поставить). Вот и у тебя в подписи какая то строка из трех иероглифов - как они уживутся в 1 байте с латинским и русским(украинским) алфавитом(раскладкой).
Автор: takedo Источник: Vingrad


Как включить/отключить юникод в проекте.

На примере Visual C++ 8.0 (входит в состав Microsoft Visual Studio 2005).
В главном меню находим "Project", заходим в <имя_вышего_проекта> properties... либо жмем Alt+F7. 
Появляется диалог настроек проекта, раскрываем ветку Configuration Properties->General (для более ранних версий студии просто General), 
ищем строку Charaster Set, выбираем для нее значение Use Multi-Byte charaster Set, для отключения юникода и, соответственно,
Use Unicode charaster Set для включения.


Использование юникодных строк.

Правильнее говорить строковые литералы, но не суть.
Строка Multi-Byte задается, как вы уже наверное знаете, так:
Код

const CHAR mbString[] = "Text";


юникодная строка так:
Код

const WCHAR uniString[] = L"Text";

те для приведенного выше примера подойдет такая запись:
Код

SetWindowText( L"Text" );



Сделать код в проекте независимым от настроек юникода

Для этого нужно обернуть все строки макросом _T() или TEXT(), а в качестве типа, представляющего отдельный символ использовать TCHAR.
Пример:
Код

const TCHAR tString[] = _T("Text");

те для приведенного выше примера подойдет такая запись:
SetWindowText( _T("Text") );
Теперь компилятор, в зависимости от настроек проекта интерпретирует строку правильно, 
то есть если юникод включен то как L"Text", а если выключен, то как "Text", 
а тип TCHAR будет понят как WCHAR или CHAR соответственно.
Тоже касается типа LPTSTR, который разворачивается в LPWSTR либо LPSTR

Практически для всех функций, принимающих строки в качестве аргументов существует два варианта - с буквами A и W на конце,
также существуют соответствующие макросы котрые разворачиваются в один из вариантов в зависимости от настроек юникода.
К примеру существуют функции: 
SetWindowTextA
SetWindowTextW
и макрос:
SetWindowText, который будет интерпретирован компилятором как один из вариантов выше.
Вывод: если делаете код независимым от настроек юникода, используйте имена функций без A и W на конце,
иначе вы только запутываете себя и окружающих.

Посмотрим что получается в нашем примере:
Код

SetWindowText( _T("Text") );

если юникод включен, то он воспринимается компилятором, как если было бы написано:
Код

SetWindowTextW( L"Text" );

если юникод выключен, то как:
Код

SetWindowTextA( "Text" );

чего мы и добивались - проект будет компилироваться в любом случае.

Следует только добавить, что для функций из Microsoft run-time library тоже существует по три вида на каждую, 
только зная один из видов не всегда легко догадаться, какие остальные два вида.
Для printf() например, подборка такая:
_tprintf - для TCHAR
 printf   - для CHAR
 wprintf - для WCHAR
для юникодо-независимого кода нужно использовать _tprintf.
чтобы узнать вид TCHAR'ного эквивалента для конкретной функции пользуйтесь MSDN 
(печатаем printf, ставим на него курсор, жмем F1).


Отличия макросов _T() и TEXT()

отличие очень тонкое. У MS есть два макроса для юникода/неюникода:
1. UNCIDOE - PSDK (Platform SDK - Windows SDK) для WinAPI функций.
2. _UNICODE- в CRT (C Runtime) для всяких _tprintf, _tcsclen и пр.

Для первого в winnt.h определяется макрос TEXT, именно он рекомендуется к использованию (так как фнукциями WinAPI пользуются чаще, хотя правда вряд ли кто-то будет определять юникод для одного и запрещать для другого  smile ). Для последнего - в tchar.h есть _T и _TEXT.

Вот так. Когда в пропертях проекта ставим юникод - в команд-лайн компилера добавляется /DUNICODE и /D_UNICODE. Потому (дополнительно) явные дефайны не рулят - надо знать, что делаем и для чего.
Автор: Любитель Источник: Vingrad


Преобразование из Unicode в Multi-Byte

Проекты у меня всегда Юникод.Но попалась функция.где алгоритм должен был работать с АНСИ строкой.
Вот так примерно я сделал.
Код

LPCWSTR domm=(LPCWSTR) user_name.GetBuffer();
int d=WideCharToMultiByte(CP_ACP,0,domm,-1,NULL,NULL,NULL,NULL);
char *temp=new char [d];
WideCharToMultiByte(CP_ACP,0,domm,-1,(LPSTR)temp,d,NULL,NULL);
CStringA nm
nm.SetString(temp);
delete []temp;

Автор: Coocky Источник: Vingrad

Не понимаю, чего вы паритесь. Строки обоих типов прекрасно друг в друга преобразовываются: у них есть шаблонные конструкторы и операторы присваивания. Вот этот код нормально компилируется и работает:

Код

   CStringA strA = "abc";
   CStringW strW(strA);
   strA = strW;


Конечно, все это правильно насчет WideCharToMultiByte и т.д., но это уже прописано внутри ATL\MFC.
Автор: Earnest Источник: Vingrad

Это сообщение отредактировал(а) zkv - 24.9.2007, 00:08
PM MAIL   Вверх
JackYF
Дата 24.9.2007, 00:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: нет
Всего: 162



Хороша подборочка smile


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
zkv
Дата 24.9.2007, 01:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Участник Клуба
Сообщений: 2133
Регистрация: 23.7.2006
Где: Санкт-Петербург

Репутация: 16
Всего: 92



Цитата(JackYF @  24.9.2007,  00:56 Найти цитируемый пост)
Хороша подборочка 

лучше критику давай  smile smile
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 09:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Все почти нормально. MBCS - не один байт, а от 1 до 2. 
Для работы с std::string рекомендуется в статье на codeproject.com что-то подобное

typedef std::basic_string<TCHAR> tstring; 

так что std::string(std::wstring) объявляются

tstring s;// пример объявления

там же прописываются разные виды win-строковых типов, а также примеры работы и преобразований (где есть сложности), а также как прописывать _T-функции (аналоги стандартной С-библиотеки для работы с строками).
вместо

char c = *i;

рекомендуется

TCHAR c = ...

Статья в двух частях, прочитать стоит. Ссылку прямо щас не найду.



Это сообщение отредактировал(а) akizelokro - 24.9.2007, 09:29


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 10:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Код

CStringA strA = "abc";
CStringW strW(strA);
strA = strW;


Пример не совсем удачен. Когда я попытался откомпилить этот кусок в ATL-проекте (без MFC) компилер выдал:
C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afx.h(24) : fatal error C1189: #error :  Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]

Это во-первых. Во-вторых, более показателен был бы пример с последовательностью
CStringA strA(strW),
так как А(1 байт)<=W(1-2 байта).
Но и здесь задача еще является достаточно упрощенной.
Более сложный вариант был бы по конвертации BSTR (в Юникодовском проекте) в std::string. И здесь уже надо применять ATL-вское
USES_CONVERSION;
SetDlgItemText(IDC_EDIT1, W2CT(bstr));

или wctomb.

Если я как всегда чтой-то не напутал. smile 



--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
zkv
Дата 24.9.2007, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Участник Клуба
Сообщений: 2133
Регистрация: 23.7.2006
Где: Санкт-Петербург

Репутация: 16
Всего: 92



Цитата(akizelokro @  24.9.2007,  10:14 Найти цитируемый пост)
так как А(1 байт)<=W(1-2 байта).

а не А(1-2 байт)<=W(2 байта) smile
Цитата(akizelokro @  24.9.2007,  10:14 Найти цитируемый пост)
Пример не совсем удачен. Когда я попытался откомпилить этот кусок в ATL-проекте (без MFC) компилер выдал:
C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afx.h(24) : fatal error C1189: #error :  Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]

посмотрю
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 10:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Ага, напутал. Thanksы. smile 

Цитата

а не А(1-2 байт)<=W(2 байта) 


Но суть та же SBCS(1 байт), MBCS(1-2 байта), UNICODE(2 байта).


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 11:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Цитата

посмотрю 

Не надо. Уже сам глянул в MSDN. Там идет #include "atlstr.h" и CAtlStringT.
Но в примере перевода BSTR через конвертацию посредством C(Atl)StringT вырастают чутка накладные расходы. Хотя, это дело программиста.


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
Ln78
Дата 24.9.2007, 13:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 274
Регистрация: 25.11.2006

Репутация: 1
Всего: 15



А насколько такая независимость от настроек универсальна?
Например, пишу длл-ку, у экспортируемых функций аргументы THAR. Отдал двоим знакомым, один из них разрабатывает приложение с определённым UNICODE, другой - нет. Неужели обоим повезёт? smile 
PM MAIL   Вверх
zkv
Дата 24.9.2007, 13:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Участник Клуба
Сообщений: 2133
Регистрация: 23.7.2006
Где: Санкт-Петербург

Репутация: 16
Всего: 92



Цитата(Ln78 @  24.9.2007,  13:06 Найти цитируемый пост)
А насколько такая независимость от настроек универсальна?
Например, пишу длл-ку, у экспортируемых функций аргументы THAR. Отдал двоим знакомым, один из них разрабатывает приложение с определённым UNICODE, другой - нет. Неужели обоим повезёт?

Придется создавать две dll'ки, не делай вид что не знаешь smile Но сделать это будет просто - достаточно изменить один пункт в настройках. 

Цитата(Ln78 @  24.9.2007,  13:06 Найти цитируемый пост)
А насколько такая независимость от настроек универсальна?

Как это сформулировать-то, проект независимый  для времени компиляции. 


Спасибо за уточнения! Обязательно внесу изменения.

Это сообщение отредактировал(а) zkv - 24.9.2007, 13:20
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Достаточно универсальна, если знать, что делать. В VS6 (например), при создании ActiveX контрола у меня появились следующие конфигурации: "win32debug", "win32debug unicode" и тд. Проверить хотя бы на уровне компиляции достаточно просто.

Неграмотное применение невстроенных типов, равно как и предположения о равенстве размера переменных типа int размеру переменных типа long на 64-разрядной платформе, я оставлю на совести программиста. Грабли можно найти и на ровном месте.



--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
Ln78
Дата 24.9.2007, 13:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 274
Регистрация: 25.11.2006

Репутация: 1
Всего: 15



Цитата(zkv @  24.9.2007,  13:18 Найти цитируемый пост)
не делай вид что не знаешь 

Да я и не делаю, я знал, что ты знаешь, что я знаю smile Просто я не совсем уверен, что в хедерах в этом случае лучше писать TCHAR, а не с указанием конкретного типа.
И ещё: раз уж упоминались WideCharToMultiByte и MultiByteToWideChar, то можно сказать "В качстве альтернативы runtime-библиотека C предоставляет более простые, компактные и платформо-независимые функции преобразования" (цитата из Трельсена). Имеются в виду функции wcstombs и mbstowcs 
PM MAIL   Вверх
akizelokro
Дата 24.9.2007, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Цитата

Просто я не совсем уверен, что в хедерах в этом случае лучше писать TCHAR, а не с указанием конкретного типа.

Только в хедерах TCHAR не пишем?  smile И пустьпотом в затылках чешут, что и почем! Так им и надо  smile 

С wcstombs тоже не все так уж гладко. Я в бета-релизе откомпилил такое:

Code.cpp(177) : warning C4996: 'wcstombs': This function or variable may be unsafe. Consider using wcstombs_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
        C:\Program Files\Microsoft Visual Studio 9.0\VC\include\stdlib.h(534) : see declaration of 'wcstombs'
 smile 


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
Ln78
Дата 24.9.2007, 15:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 274
Регистрация: 25.11.2006

Репутация: 1
Всего: 15



akizelokro, а ты там же никогда sprintf не компилировал, не видел аналогичного замечания? Вряд ли это имеет самое прямое отношение к char/wchar (хотя тоже новшество студии 2005).
Про хедеры: обычно, когда библиотека распространяется, передаются часть *.h-файлов, *.lib и *.dll. Поэтому я и сказал про хедеры - имея в виду для тех функций, которые являются вызываемыми из других библиотек или приложений.
PM MAIL   Вверх
akizelokro
Дата 25.9.2007, 07:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: нет
Всего: 5



Ладно, со sprintf почти согласен. Только две поправки. Лучше применять тогда уж вообще snprintf, а в данном конкретном случае _sntprintf, потому что в подобном стиле оформления можно практически полностью избежать конвертации (и тем самым, снять 95% вопроса).
Про хедеры - тоже замётано. Но я уже говорил, тут прежде всего надо знать, что делать. Можно создать проблем другим участникам проекта и без всего, о чем мы говорим. Согласен?



--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
zkv
Дата 25.9.2007, 10:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Участник Клуба
Сообщений: 2133
Регистрация: 23.7.2006
Где: Санкт-Петербург

Репутация: 16
Всего: 92



Предлагаю sprintf больше не обсуждать smile
По поводу хедеров, по мне удобнее когда хедеры одни и теже в библиотеке, а отличаются только либ-файлы и длл, опять таки, достаточно изменить пару настроек в проекте - и все работает в другом charaster set. 
Причем многие библиотеки так и поступают.
И если что линкер либу не пропустит неправильную (если не голая дллка, конечно).

Корректировки внесу скоро.
PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0983 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.