Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

В Windows 98 основную работу выполняет CreateWindowExA. В этой операцион ной системе предусмотрены точки входа для всех функций Windows, принимающих Unicodeстроки, но функции не транслируют их в ANSi, а просто сообщают об ошибке.

Последующий вызов GetLastError дает ERRORCALL_NOT_IMPLEMENTED. Должным образом действуют только ANSI-версии функций. Ваше приложение не будет работать в Windows 98, если в скомпилированном коде присутствуют вызовы "широкосимвольных" функций.

Некоторые функции Windows API (например, WinExec или OpenFile) существуют только для совместимости с 16-разрядными программами, и их надо избегать. Лучше заменить все вызовы WinExec и OpenFile вызовами CreateProcess и CreateFile соответственно. Тем более, что старые функции просто обращаются к новым. Самая серьезная проблема с ними в том, что они не принимают строки в Unicode, при их вызове Вы должны передавать строки в ANSI. С другой стороны, в Windows 2000 у всех новых или пока не устаревших функций обязательно есть как ANSI-, так и Unicode-версия.

Строковые функции Windows

Windows предлагает внушительный набор функций, работающих со строками. Они похожи на строковые функции из библиотеки С, например на strcpy и wcscpy. Однако функции Windows являются частью операционной системы, и многие се компоненты используют именно их, а нс аналоги из библиотеки С. Я советую отдать предпочтение функциям операционной системы. Это немного повысит быстродействие Вашего приложения. Дело в том, что к ним часто обращаются такие тяжеловесные процессы,

как оболочка операционной системы (Explorer.exe), и скорее всего эти функции будут загружены в память еще до запуска Вашего приложения.

NOTE:

Данные функции доступны в Windows 2000 и Windows 98. Но Вы сможете вызывать их и в более ранних версиях Windows, если установите Internet Explorer версии 4.0 или выше.

По классической схеме именования функций в операционных системах их имена состоят из символов нижнего и верхнего регистра и выглядят так; StrCat, StrChr, StrCmp, StrCpy и т. д. Для использования этих функций включите в программу заголовочный файл ShlWApi.h. Кроме того, как я уже говорил, каждая строковая функция существует в двух версиях — для ANSI и для Unicode (например, StrCatA и StrCatW).

Поскольку это функции операционной системы, их имена автоматически преобразуются в нужную форму, если в исходном тексте Вашей программы перед ее сборкой определен

UNICODE.

Создание программ, способных использовать и

ANSI, и Unicode

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

привыкайте к тому, что текстовые строки — это массивы символов, а не массивы байтов или значений типа char;

используйте универсальные типы данных (вроде TCHAR или PTSTR) для текстовых символов и строк;

используйте явные типы данных (вроде BYTE или PBYTE) для байтов, указателей на байты и буферов данных;

применяйте макрос _TEXT для определения символьных и строковых литералов; предусмотрите возможность глобальных замен (например, PSTR на PTSTR); модифицируйте логику строковой арифметики. Например, функции обычно принимают размер буфера в символах, а не в байтах Это значит, что вместо sizeof(szBuffer) Вы должны передавать (sizeof(szBuffer) / sizeof(TCHAR)). Но блок памяти для строки известной длины выделяется в байтах, а не символах, т. e.

вместо malloc(nCharacters) нужно использовать malloc(nCbaracters *sizeof(TCHAR))

Из всего, что я перечислил, это запомнить труднее всего — если Вы ошибетесь, компилятор не выдаст никаких предупреждений.

Разрабатывая программы-примеры для первого издания книги, я сначала написал их так, что они компилировались только с использованием ANSI. Но, дойдя до этой главы (она была тогда в конце), понял, что Unicode лучше, и решил написать примеры, которые показывали бы, как легко создавать программы, компилируемые с применением и Unicode, и ANSI. B конце концов я преобразовал все программы-примеры так, чтобы их можно было компилировать в расчете на любой из этих стандартов.

Конверсия всех программ заняла примерно 4 часа — неплохо, особенно если учесть, что у меня совсем не было опыта в этом деле.

В Windows есть набор функций для работы с Unicode-строками. Эти функции перечислены ниже.

Функция

Описание

 

 

lstrcat

Выполняет конкатенацию строк

 

 

lstrcmp

Сравнивает две строки с учетом регистра букв

 

 

lstrcmpi

Сравнивает две строки без учета регистра букв

 

 

lstrcpy

Копирует строку в другой участок памяти

 

 

lstrlen

Возвращает длину строки в символах

 

 

Они реализованы как макросы, вызывающие либо Unicode-, либо ANSI-версию функции в зависимости от того, определен ли UNICODE при компиляции исходного модуля Например, если UNICODE не определен, lstrcat раскрывается в lstrcatA, определен — в lstrcatW.

Строковые функции lstrcmp и lstrcmpi ведут себя не так, как их аналоги из библиотеки С (strcmp, strcmpi, wcscmp и wcscmpf), которые просто сравнивают кодовые позиции в символах строк. Игнорируя фактические символы, они сравнивают числовое значение каждого символа первой строки с числовым значением символа второй

строки. Но lstrcmp и lstrcmpi реализованы через вызовы Windows-функции CompareString;

int CompareString( LCID lcid,

DWORD fdwStyle, PCWSTR pString1, int cch1,

PCWSTR pString2, int cch2);

Она сравнивает две Unicode-строки. Первый параметр задаст так называемый идентификаторлокализации (locale ID, LCID) — 32-битное значение, определяющее конкретный язык. С помощью этого идентификатора CompareString сравнивает строки с учетом значения конкретных символов в данном языке. Так что она действует куда осмысленнее, чем функции библиотеки С.

Когда любая из функций семейства lstrcmp вызывает CompareString, в первом параметре передается результат вызова Windows-функции GetThreadLocale.

LCID GetThreadLocale();

Она возвращает уже упомянутый идентификатор, который назначается потоку в момент его создания.

Второй параметр функции CompareString указывает флаги, модифицирующие метод сравнения строк. Допустимые флаги перечислены в следующей таблице.

Флаг

Действие

 

 

NORM_IGNORECASE

Различия в регистре букв игнорируются

 

 

NORM_IGNOREKANATYPE

Различия между знаками хираганы и катаканы игнорируются

 

 

NORM_IGNORENONSPACE

Знаки, отличные от пробелов, игнорируются

 

 

NORM_IGNORESYMBOLS

Символы, отличные от алфавитно-цифровых, игнорируются

 

 

NORM_IGNOREWIDTH

Разница между одно- и двухбайтовым представлением

 

одного

 

и того же символа игнорируется

SORT_STRINGSORT

Знаки препинания обрабатываются так же, как и символы, от-

 

личные от алфавитно-цифровых

 

 

Вызывая CompareString, функция lstrcmp передает в параметре fdwStyle нуль, а lstrcmpi —

флаг NORM_IGNORECASE. Остальные четыре параметра определяют две строки и их длину. Если cch1 равен -1, функция считает, что строка pString2 завершается нулевым символом, и автоматически вычисляет ее длину. То же относится и к параметрам cch2 wpString2.

Многие функции С-библиотеки с Unicode-строками толком не работают. Так, tolower и toupper неправильно преобразуют регистр букв со знаками ударения. Поэтому для Unicode-строк лучше использовать соответствующие Windows-функции. К тому же они корректно работают и с ANSI-строками.

Первые две функции:

PTSTR CharLower(PTSTR pszStnng);

PTSTR CharUpper(PTSTR pszString);

преобразуют либо отдельный символ, либо целую строку с нулевым символом в конце. Чтобы преобразовать всю строку, просто передайте ее адрес. Но, преобразуя отдельный символ, Вы должны передать его так:

TCHAR cLowerCaseCnr = CharLower((PTSTR) szString("O"));

Приведение типа отдельного символа к PTSTR вызывает обнуление старших 16 битов передаваемого значения, а в его младшие 16 битов помещается сам символ. Обнаружив,

что старшие 16 битов этого значения равны 0, функция поймет, что Вы хотите преобразовать не строку, а отдельный символ. Возвращаемое 32-битное зна чение содержит результат преобразования в младших 16 битах.

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

DWORD CharLowerBuff(

PTSTR pszString,

DWORD cchString);

DWORD CharUppprRuff(

PTSTR pszString,

DWORD cchString);

Прочие функции библиотеки С (например, isalpba, islowern isupper) возвращают значение, которое сообщает, является ли данный символ буквой, а также строчная она или прописная. В Windows API тоже есть подобные функции, но они учитывают и язык, выбранный пользователем в Control Panel:

BOOL IsCharAlpha(TCHAR ch);

BOOL IsCharAlphaNumeric(TCHAR ch);

BOOL IsCharLower(TCHAR oh);

BOOL IsCharUpper(TCHAR ch);

И последняя группа функций из библиотеки С, о которых я хотел рассказать, — prmtf, Если при компиляции _UNICODE определен, они ожидают передачи всех символьных и строковых параметров в Unicode; в ином случае — в ANSI.

Microsoft ввела в семейство фупкций printf своей С-библиотеки дополнительные типы полей, часть из которых не поддерживается в ANSI C. Они позволяют легко сравнивать и смешивать символы и строки с разной кодировкой. Также расширена функция wsprintf операционной системы. Вот несколько примеров (обратите внимание на использование буквы s в верхнем и нижнем регистре):

char szA[100]; // строковый буфер e ANSI WCHAR szW[100]; // строковый буфер в Unicode

//обычный вызов sprintf: все строки в ANSI sprintf(szA, "%s", "ANSI Str");

//преобразуем строку из Unicode в ANSI sprintf(szA, "%S", "Unicode Str");

//обычный вызов swprintf. все строки в Unicode swprintf(szW, L"%s", L"Unicode Str");

//преобразуем строку из ANSI в Unicode swprintf(s/W, L"%S", "ANSI Str");

Ресурсы

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