Джонсон Харт - Системное программирование в среде Windows
В Windows также существует группа функций, предназначенных для работы с символами и строками в представлении Unicode. Эти функции обеспечивают прозрачную обработку региональных особенностей. Типичными функциями этой группы являются функция CharUpper, которую можно применять как к строкам, так и к отдельным символам, и функция IsCharAlphaNumeric. К числу других функций для работы со строками принадлежат функция CompareString (учитывающая особенности локализации) и функция MultiByteToWideChar. Многобайтовые символы Windows 3.1 и 9х расширяют наборы 8-битовых символов, позволяя применять для представления наборов символов, используемых в языках дальневосточных стран, сдвоенные байты. Чтобы продемонстрировать использование функций обоих типов, будут рассмотрены примеры программ, в которых используются как обобщенные функции библиотеки С (_tprintf и подобные ей), так и функции Windows (CharUpper и подобные ей). Примеры в последующих главах в основном опираются на обобщенную библиотеку С.
Обобщенная функция Main
Обозначение С-функции main с ее списком аргументов (argv[]) следует заменить макросом _tmain. В зависимости от определения символической константы _UNICODE макрос разворачивается либо до main, либо до wmain. _tmain определяется в заголовочном файле <tchar.h>, который следует включать после файла <windows.h>. Тогда типичный заголовок основной программы будет иметь следующий вид:
#include <windows.h>
#include <tchar.h>
int _tmain(int argc, LPTSTR argv[]) {
…
}
В Microsoft С функция _tmain поддерживает дополнительный третий параметр, используемый для строк окружения. Такое нестандартное расширение является обычным в UNIX.
Определения функций
В качестве примера рассмотрим функцию CreateFile. Если символьная переменная UNICODE определена, то эта функция определяется как CreateFileA, а если не определена — то как CreateFileW. Строковые параметры в объявлениях также описываются как строки 8-битовых символов или символов в расширенной форме. Следовательно, если в исходном коде присутствуют такие, например, ошибки, как использование неподходящих параметров в функции CreateFile, то в сообщениях компилятора об этих ошибках будут указываться функции CreateFileA или CreateFileW.
Стратегии использования символов Unicode
Приступая к работе над проектом в Windows, либо для разработки нового программного кода, либо для переноса существующего, программист, в зависимости от требований проекта, может выбрать одну из четырех стратегий.
1. Только 8-битовые символы. Игнорируйте Unicode и продолжайте использовать для таких функций, как printf, atoi и strcmp, типы данных char (или CHAR) и стандартную библиотеку С.
2. 8-битовые символы, но с возможностью использования символов Unicode. Следуйте ранее данным рекомендациям в отношении обобщенных приложений, но не определяйте константы UNICODE и _UNICODE директивами препроцессора. В приведенных в данной книге примерах программ используется именно эта стратегия.
3. Только символы Unicode. Следуйте рекомендациям в отношении обобщенных приложений, но при этом определите директивами препроцессора обе константы UNICODE и _UNICODE. Другой возможный вариант состоит в том, чтобы использовать исключительно расширенную форму символов и функций для работы с символами. Результирующие программы не смогут правильно выполняться под управлением Windows 9x.
4. Символы Unicode и 8-битовые символы. Программа ориентируется на работу как с символами Unicode, так и с ASCII-символами, причем решение относительно того, какие участки программного кода должны работать, принимается программой на стадии выполнения с использованием переключателей времени выполнения или других возможных средств.
Как уже отмечалось ранее, несмотря на то что написание обобщенного кода требует дополнительных усилий, а результирующая программа становится менее удобочитаемой, эта мера позволяет программисту добиться максимальной гибкости приложения.
Параметры локализации могут устанавливаться во время выполнения программы. В программе 2.2 показано, как определить язык, который должен использоваться в сообщениях об ошибках.
Стандарт локализации приложений POSIX XPG4, предоставляемый многими поставщиками UNIX, существенно отличается от стандарта Unicode. Помимо всего прочего, символы в этом стандарте могут представляться 4, 3 или 1 байтами в зависимости от контекста, особенностей локализации и так далее.
Microsoft С реализует функции стандартной библиотеки С, среди которых имеются также версии, рассчитанные на работу с символами в расширенной форме. Так, заголовочный файл <wchar.h> содержит описание функции _tsetlocale. В Windows NT используются символы Unicode, тогда как в Windows 9x используются те же многобайтовые символы (смесь 8– и 16-битовых символов), что и в Windows 3.1.
Стандартные устройства и консольный ввод/вывод
Как и в UNIX, в Windows предусмотрены три стандартных устройства, предназначенные, соответственно, для ввода данных (input), вывода данных (output) и вывода сообщений об ошибках (error). В UNIX для этих устройств используются известные системе значения дескрипторов файлов (0, 1 и 2), однако в Windows доступ к стандартным устройствам осуществляется с помощью дескрипторов типа HANDLE, для получения которых предоставляется специальная функция.
HANDLE GetStdHandle(DWORD nStdHandle)
Возвращаемое значение: в случае успешного выполнения — действительный дескриптор, иначе — значение INVALID_HANDLE_VALUE.
ПараметрыПараметр nStdHandle должен принимать одно из следующих значений:
• STD_INPUT_HANDLE
• STD_OUTPUT_HANDLE
• STD_ERROR_HANDLE
В качестве стандартных устройств обычно назначаются консоль и клавиатура. Стандартный ввод/вывод можно перенаправлять на другие устройства.
Вызов функции GetStdHandle не приводит к созданию новых или дублированию существующих дескрипторов стандартных устройств. Последовательные вызовы с указанием в качестве аргумента одного и того же устройства будут возвращать одно и то же значение дескриптора. Закрытие дескриптора стандартного устройства делает это устройство недоступным для дальнейшего использования. По этой причине в примерах ниже мы будем часто открывать дескриптор стандартного устройства, но не закрывать его.
BOOL SetStdHandle(DWORD nStdHandle, HANDLE hHandle)
Возвращаемое значение: в случае успешного выполнения — TRUE, иначе — FALSE.
ПараметрыДопустимые значения параметра nStdHandle функции SetStdHandle являются теми же, что и в случае функции GetStdHandle. Параметр hHandle указывает открытый файл, который назначается в качестве стандартного устройства.
Одним из методов перенаправления стандартного ввода/вывода является последовательный вызов функций SetStdHandle и GetStdHandle. Полученный в результате этого дескриптор используется в последующих операциях ввода/ вывода.
Для указания путей доступа к консольному вводу (клавиатуре) и консольному выводу предусмотрены два зарезервированных имени: "CONIN$" и "CONOUT$". Роль стандартных устройств ввода, вывода и вывода ошибок первоначально отводится консоли. Однако консоль можно использовать даже после того, как операции ввода/вывода, требующие применения стандартных устройств, будут перенаправлены; для этого требуется лишь открыть дескрипторы для файлов "CONIN$" и "CONOUT$", вызвав функцию CreateFile.
В UNIX стандартный ввод/вывод может быть перенаправлен одним из трех способов (см. [40], стр. 61—64).
Первый метод является косвенным и основывается на том, что функция dup возвращает дескриптор файла с наименьшим доступным номером. Предположим, вы хотите переназначить стандартный ввод (файловый дескриптор 0) открытому файлу, описанному как fd_redirect. Тогда можно записать следующий код:
close (STDIN_FILENO);
dup (fd_redirect);
Во втором методе используется функция dup2, а третий метод предполагает вызов замысловатой перегруженной функции fcntl с использованием в качестве параметра значения F_DUPFD.
Операции консольного ввода/вывода могут выполняться с помощью функций ReadFile и WriteFile, но проще использовать предназначенные специально для этого функции консоли ReadConsole и WriteConsole. Основное преимущество этих функций заключается в том, что они манипулируют не байтами, а обобщенными символами (TCHAR), и, кроме того, обрабатывают символы в соответствии с текущими режимами консоли, которые устанавливаются функцией SetConsoleMode.
BOOL SetConsoleMode(HANDLE hConsoleHandle, DWORD fdevMode)
Возвращаемое значение: тогда, и только тогда, когда функция завершается успешно — TRUE, иначе — FALSE.
ПараметрыhConsoleHandle — дескриптор буфера ввода консоли или буфера дисплея, который должен быть создан с правами доступа GENERIC_WRITE, даже если устройство предназначено только для ввода информации.