4 Национальные параметрыОперационная система Microsoft Windows NT разрабатывалась таким образом, чтобы максимально облегчить разработку приложений, адаптированных для использования в различных странах (как сейчас модно говорить, локализованных приложений). В чем здесь проблема? В каждой стране используется свой национальный язык, свои символы, своя система обозначения времени и даты и так далее. Если бы разработчики пытались учесть в своих приложениях все национальные особенности, им бы пришлось затратить немало времени на создание приложений. К счастью, в операционной системе Microsoft Windows NT имеется специальный программный интерфейс, значительно облегчающий процедуру создания “интернациональных” приложений. Наборы национальных параметровПри установке Microsoft Windows NT пользователь может указать, какие наборы национальных параметров должны быть установлены (для каких стран). Российские пользователи в своем большинстве пожелают установить по крайней мере английский и русский набор параметров, для того чтобы было можно работать в английской и русской среде. Некоторым дополнительно могут потребоваться национальные параметры для работы с французским, немецким или каким-либо другим языком. С точки зрения разработчика приложений наборы параметров обозначаются при помощи так называемого идентификатора наборов национальных параметров LCID (в документации к SDK этот термин звучит как locale identifier). Многозадачная операционная система Microsoft Windows NT допускает установку отдельного набора национальных параметров для каждой задачи. Для этого предназначена функция с именем SetThreadLocale. Что же касается Microsoft Windows 95, то в среде этой операционной системы соответствующих средств нет - пользователь должен выбрать только один набор при помощи приложения Control Panel (пиктограмма Regional Settings). Новый набор параметров будет использован после перезагрузки. Рассмотрим кратко средства, предназначенные для работы с наборами национальных параметров. Установка набора национальных параметровКак мы только что сказали, в операционной системе Microsoft Windows NT для установки набора национальных параметров, используемых текущей задачей, эта задача должна вызвать функцию SetThreadLocale: BOOL SetThreadLocale( LCID Locale); // идентификатор национального набора Работа с функцией SetThreadLocale проста - вам достаточно передать ей нужный идентификатор в качестве параметра. При успехе функция возвратит значение TRUE, а при ошибке - FASLE. В последнем случае вы можете определить код ошибки с помощью функции GetLastError. Как задавать идентификатор национального набора параметров? Вы можете передать функции SetThreadLocale либо одну из констант, либо значение, полученное от макрокоманды MAKELCID. Список констант приведен ниже.
Макрокоманда MAKELCID позволяет составить идентификатор национального набора параметров LCID из двух значений: идентификатора национального языка и идентификатора метода сортировки. Ниже мы привели прототип этой макрокоманды и ее определение: DWORD MAKELCID( WORD wLanguageID, // идентификатор национального языка WORD wSortID); // идентификатор метода сортировки #define MAKELCID(lgid, srtid) \ ((DWORD)((((DWORD)((WORD)(srtid))) << 16) \ | ((DWORD)((WORD)(lgid))))) Что касается идентификатора сортировки, то здесь вы должны указывать значение SORT_DEFAULT. А для создания идентификатора национального языка вам придется воспользоваться еще одной макрокомандой с именем MAKELANGID: WORD MAKELANGID( USHORT usPrimaryLanguage, // первичный идентификатор языка USHORT usSubLanguage); // вторичный идентификатор языка #define MAKELANGID(usPrimaryLanguage, usSubLanguage) \ ((((WORD)( usSubLanguage)) << 10) \ | (WORD)( usPrimaryLanguage)) Первичный идентификатор задает национальный язык, а вторичный - его диалект или разновидность. Для первичного идентификатора вы можете указать одно из следующих значений:
Ниже мы привели список допустимых вторичных идентификаторов:
Заметим, что хотя приложение может указывать любые из перечисленных выше идентификаторов национальных языков, функция SetThreadLocale сможет установить только те, что были выбраны при установке операционной системы Microsoft Windows NT. И еще одно замечание. Если в качестве первичного идентификатора языка указать константу LANG_NEUTRAL, то комбинации с идентификаторами SUBLANG_NEUTRAL, SUBLANG_DEFAULT и SUBLANG_SYS_DEFAULT будут иметь специальное значение, как это показано ниже:
Ниже мы привели пример использования функции SetThreadLocale для установки английского и русского наборов национальных параметров: // Установка английского набора параметров fRc = SetThreadLocale(MAKELCID( MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT)); // Установка русского набора параметров fRc = SetThreadLocale(MAKELCID( MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT)); Определение национального набора параметровПомимо установки нового набора национальных параметров, часто возникает и обратная задача - определение текущего набора национальных параметров, или определение набора, который используется по умолчанию операционной системой или текущим пользователем. Для решения этой задачи в программном интерфейсе операционной системы Microsoft Windows NT предусмотрен набор функций и макрокоманд, к рассмотрению которых мы сейчас переходим. Определение текущего набора национальных параметров для задачиВ любой момент времени задача может получить установленный для нее идентификатор текущего набора установленных параметров, если воспользуется функцией GetThreadLocals: LCID GetThreadLocale(VOID); Эта функция не имеет параметров. Она возвращает 32-разрядный идентификатор национального набора параметров, из которого при помощи различных макрокоманд можно выделить различные компоненты. С помощью макрокоманды LANGIDFROMLCID вы можете выделить из идентификатора набора национальных параметров идентификатор национального языка: WORD LANGIDFROMLCID( LCID lcid); // идентификатор набора национальных параметров #define LANGIDFROMLCID(lcid) ((WORD)(lcid)) Далее, с помощью макрокоманды PRIMARYLANGID нетрудно выделить из идентификатора национального языка первичный идентификатор языка: WORD PRIMARYLANGID( WORD lgid); // идентификатор национального языка #define PRIMARYLANGID(lgid) ((WORD)(lgid) & 0x3ff) Аналогично, макрокоманда SUBLANGID XE "SUBLANGID" позволяет выделить из идентификатора национального языка вторичный идентификатор языка (диалект): WORD SUBLANGID( WORD lgid); // идентификатор национального языка #define SUBLANGID(lgid) ((WORD)(lgid) >> 10) Определение набора национальных параметров по умолчаниюЕсли приложению нужно использовать наборы национальных параметров, которые используются операционной системой по умолчанию или которые установлены для текущего пользователя по умолчанию, оно также может использовать специальные функции. Функция GetSystemDefaultLCID не имеет параметров и возвращает идентификатор набора национальных параметров, которые используются операционной системой по умолчанию: LCID GetSystemDefaultLCID(VOID); Для определения идентификатора набора национальных параметров, установленных по умолчанию для текущего пользователя Microsoft Windows NT вы должны вызвать функцию GetUserDefaultLCID, которая также не имеет параметров: LCID GetUserDefaultLCID(VOID); Функция GetSystemDefaultLangID позволяет определить идентификатор национального языка, который используется операционной системой по умолчанию: LANGID GetSystemDefaultLangID(VOID); С помощью функции GetUserDefaultLangID приложение может определить идентификатор национального языка, который установлен по умолчанию для текущего пользователя Microsoft Windows NT: LANGID GetUserDefaultLangID(VOID); Определение отдельных национальных параметровТеперь, когда мы научились устанавливать и определять идентификатор национальных параметров, пора перейти к самим национальным параметрам. Для определения значений отдельных параметров для заданного идентификатора национальных параметров вы должны использовать функцию GetLocaleInfo: int GetLocaleInfo( LCID Locale, // идентификатор набора параметров LCTYPE LCType, // тип информации LPTSTR lpLCData, // адрес буфера для информации int cchData); // размер буфера для информации Параметр Locale этой функции задает идентификатор национальных параметров, для которого нужно определить один из конкретных параметров. Нужный национальный параметр задается параметром LCType функции GetLocaleInfo. Немного позже мы приведем сокращенный список допустимых значений для этого параметра. Полученная информация будет записана в буфер, адрес которого задается параметром lpLCData, а размер - параметром cchData. Информация будет записана в буфер в виде текстовой строки. Обычно буфер заказывается динамически, причем для определения требуемого размера буфера достаточно указать значение параметра lpLCData, равное NULL, - в этом случае функция GetLocaleInfo вернет нужный размер буфера в байтах. В случае успешного выполнения функция GetLocaleInfo возвращает размер текстовой строки с информацией, записанной в буфер lpLCData. При ошибке возвращается нулевое значение. Для типа информации LCType можно задавать очень много значений. Все допустимые значения описаны в документации SDK. Из-за ограниченного объема книги мы не имеем возможности все их перечислить, поэтому ограничимся только самыми интересными: · LOCALE_ILANGUAGE Идентификатор национального языка (длиной не более 5 символов) · LOCALE_SLANGUAGE Полное название национального языка · LOCALE_SENGLANGUAGE Полное английское название языка · LOCALE_SABBREVLANGNAME Сокращенное трехсимвольное название языка · LOCALE_SNATIVELANGNAME Естественное названия языка · LOCALE_ICOUNTRY Код страны (длиной не более 6 символов) · LOCALE_SCOUNTRY Полное локализованное название страны · LOCALE_SENGCOUNTRY Полное английское название страны · LOCALE_SABBREVCTRYNAME Сокращенное название страны · LOCALE_SNATIVECTRYNAME Естественное название страны · LOCALE_IDEFAULTLANGUAGE Идентификатор основного языка, который используется в данной стране · LOCALE_IDEFAULTCOUNTRY Основной код страны · LOCALE_IDEFAULTCODEPAGE Номер кодовой страницы OEM · LOCALE_IDEFAULTANSICODEPAGE Номер кодовой страницы ANSI · LOCALE_SLIST Символ, который используется для разделения элементов списка · LOCALE_IMEASURE Система измерений (0 - метрическая, 1 - американская) · LOCALE_SDECIMAL Символ, который используется в качестве десятичного разделителя в числах · LOCALE_STHOUSAND Символ, который используется в качестве разделителя групп цифр в многозначных числах · LOCALE_SDATE Символ-разделитель в строке даты · LOCALE_STIME Символ-разделитель в строке времени · LOCALE_IDATE Порядок, в котором располагаются компоненты даты: 0: Месяц-День-Год, 1: День-Месяц-Год, 2: Год-Месяц-День · LOCALE_SDAYNAME1 Естественное длинное название для понедельника · LOCALE_SDAYNAME2 - LOCALE_SDAYNAME7 Естественное длинное название для дней недели от вторника до воскресения · LOCALE_SABBREVDAYNAME1 Естественное сокращенное название для понедельника · LOCALE_SABBREVDAYNAME2 - LOCALE_SABBREVDAYNAME7 Естественное сокращенное название для дней недели от вторника до воскресения · LOCALE_SMONTHNAME1 Естественное длинное название для января · LOCALE_SMONTHNAME2 - LOCALE_SMONTHNAME12 Естественное длинное название для месяцев от февраля до декабря Помимо перечисленных, предусмотрены многочисленные константы для определения формата отображения даты, времени и денежных единиц, положительных и отрицательных чисел и так далее. В качестве примера использования функции GetLocaleInfo приведем следующий фрагмент кода, в котором мы определяем полное название национального языка для текущей задачи: GetLocaleInfo( GetThreadLocale(), LOCALE_SLANGUAGE, szBuf, 512); Здесь полученное название языка будет записано в виде текстовой строки в буфер szBuf. Форматное преобразование даты и времениВ этом разделе мы рассмотрим две функции, предназначенные для получения текстовых строк времени и даты. Формат этих строк зависит от указанного идентификатора набора национальных параметров. Преобразование времениС помощью функции GetTimeFormat вы можете получить текстовую строку времени: int GetTimeFormat( LCID Locale, // идентификатор набора параметров DWORD dwFlags, // флаги режимов работы функции CONST SYSTEMTIME *lpTime, // время LPCTSTR lpFormat, // строка формата времени LPTSTR lpTimeStr, // буфер для полученной строки времени int cchTime); // размер буфера в байтах Через параметр Locale вы должны передать функции GetTimeFormat идентификатор набора национальных параметров, для которого необходимо выполнить форматирование строки времени. Параметр dwFlags определяет режимы работы функции. Для этого параметра вы можете указать следующие значения:
Параметр lpTime может принимать значение NULL или содержать указатель на заполненную структуру типа SYSTEMTIME. В первом случае после завершения работы функции в буфере lpTimeStr будет получена строка для текущего времени. Во втором случае строка будет соответствовать времени, записанному в структуре SYSTEMTIME. Приведем формат структуры SYSTEMTIME: typedef struct _SYSTEMTIME { WORD wYear; // год WORD wMonth; // месяц (1 - январь, 2 - февраль // и так далее) WORD wDayOfWeek; // день недели (0 - воскресение. // 1 - понедельник, и так далее) WORD wDay; // день месяца WORD wHour; // часы WORD wMinute; // минуты WORD wSecond; // секунды WORD wMilliseconds; // миллисекунды } SYSTEMTIME; Параметр lpFormat задает строку формата, в соответствии с которым будет отформатирована выходная строка. Если этот параметр указан как NULL, будет использован стандартный формат, принятый для данного идентификатора набора национальных параметров, указанных функции GetTimeFormat в параметре Locale. В противном случае строка формата должна быть сформирована приложением. Строка формата времени может содержать специальные символы, пробелы и произвольные символы, заключенные в кавычки. Пробелы и произвольные символы будут появляться в выходной строке в указанном месте. Вместо специальных символов будут вставлены отдельные компоненты времени:
Параметры lpTimeStr и cchTime указывают, соответственно, адрес и размер буфера, в который будет записана отформатированная строка. Если параметр cchTime равен нулю, функция GetTimeFormat вернет размер буфера, достаточный для записи полной выходной строки. Ниже мы привели пример использования функции GetTimeFormat для получения текущего времени. При этом мыуказали формат, принятый по умолчанию для идентификатора набора национальных параметров, установленного в текущей задаче: GetTimeFormat(GetThreadLocale(), LOCALE_NOUSEROVERRIDE, NULL, NULL, szBuf, 512); Преобразование датыОтформатированную в соответствии с указанным набором национальных параметров текстовую строку даты вы можете получить от функции GetDateFormat, во многом аналогичной только что рассмотренной функции GetTimeFormat. Приведем прототип функции GetDateFormat: int GetDateFormat( LCID Locale, // идентификатор набора параметров DWORD dwFlags, // флаги режима работы функции CONST SYSTEMTIME *lpDate, // дата LPCTSTR lpFormat, // строка формата даты LPTSTR lpDateStr, // буфер для записи выходной строки int cchDate); // размер выходного буфера в байтах Рассмотрим отличия этой функции от функции GetTimeFormat. Ниже мы привели набор констант, которые можно использовать для параметра dwFlags. Эти константы отличаются о тех, что можно использовать с функцией GetTimeFormat XE "GetTimeFormat" :
Отличаются также специальные символы, которые можно использовать в строке формата даты:
Пример использования функции GetDateFormat для получения строки текущей даты приведен ниже: GetDateFormat(GetThreadLocale(), LOCALE_NOUSEROVERRIDE | DATE_LONGDATE, NULL, NULL, szBuf1, 512); Изменение раскладки клавиатурыПомимо изменения текущего набора национальных параметров приложение может изменить раскладку клавиатуры. При этом изменится расположение символов на клавиатуре и генерируемые при нажатии на клавиши символьные коды. Для изменения раскладки клавиатуры в программном интерфейсе операционной системы Microsoft Windows NT предусмотрено несколько специальных функций. Получение списка установленных раскладокПри установке операционной системы Microsoft Windows NT или Microsoft Windows 95 пользователь может выбрать для работы одну или несколько раскладок клавиатуры. Для определения списка установленных раскладок вы можете использовать функцию GetKeyboardLayoutList: UINT GetKeyboardLayoutList( int nBuff, // количество элементов в буфере HKL *lpList); // указатель на буфер Функция GetKeyboardLayoutList записывает в буфер, адрес которого задан параметром lpList, массив идентификаторов установленных раскладок клавиатуры, имеющих тип HKL. Через параметр nBuff вы должны передать функции размер буфера, указанный в количестве идентификаторов типа HKL. Как определить этот размер? Для этого достаточно указать функции GetKeyboardLayoutList параметр nBuff, имеющий нулевое значение. При этом функция вернет размер массива, необходимый для записи в него всех идентификаторов. Вот фрагмент кода, в котором приложение заказывает динамически память для идентификаторов установленных раскладок клавиатуры, а затем заполняет полученный буфер с помощью функции GetKeyboardLayoutList: UINT uLayouts; HKL *lpList; uLayouts = GetKeyboardLayoutList(0, NULL); lpList = malloc(uLayouts * sizeof(HKL)); uLayouts = GetKeyboardLayoutList(uLayouts, lpList); Заметим, что младшее слово идентификатора раскладки клавиатуры содержит идентификатор набора национальных параметров. Этот факт можно использовать для определения названия национального языка или страны, соответствующего данной раскладке клавиатуры. Операция определения названия национального языка выполняется в приведенном ниже фрагменте кода: GetLocaleInfo(MAKELCID(((UINT)hklCurrent & 0xffffffff), SORT_DEFAULT), LOCALE_SLANGUAGE, szBuf, 512); Определение названия текущей раскладки клавиатурыДля определения названия текущей (активной) расклдаки клавиатуры вы можете воспользоваться функцией GetKeyboardLayoutName: BOOL GetKeyboardLayoutName( LPTSTR pwszKLID); // адрес буфера для имени раскладки Через единственный параметр вы должны передать этой функции адрес буфера, в который будет записано название раскладки. Размер буфера должен быть не меньше чем KL_NAMELENGTH байт. Определение идентификатора раскладки клавиатуры для задачиС помощью функции GetKeyboardLayout приложение может определить идентификатор раскладки клавиатуры, назначенный для данной задачи: HKL GetKeyboardLayout( DWORD dwLayout); // идентификатор задачи Единственный параметр этой функции определяет идентификатор задачи, для которой нужно определить идентификатор раскладки клавиатуры. Если вы укажите нулевое значение, функция возвратит идентификатор раскладки для текущей задачи. Загрузка раскладки клавиатурыС помощью функции LoadKeyboardLayout вы можете загрузить новую раскладку клавиатуры: HKL LoadKeyboardLayout( LPCTSTR pwszKLID, // адрес буфера названия раскладки UINT Flags); // флаги режима работы функции При помощи параметра pwszKLID задается имя загружаемой раскладки. Это имя должно задаваться в виде текстовой строки, содержащей значение идентификатора национального языка в текстовом виде. Для загрузки, например, американской раскладки клавиатуры необходимо задать строку 00000409, а для загрузки русской раскладки - строку 00000419. Параметр Flags задает режимы работы функции LoadKeyboardLayout и может иметь следующие значения:
Выгрузка раскладки клавиатурыЗная идентификатор загруженной ранее расклдаки клавиатуры, вы можете выгрузить эту раскладку из памяти. Для этого следует вызвать функцию UnloadKeyboardLayout: BOOL UnloadKeyboardLayout( HKL hkl); // идентификатор выгружаемой раскладки В качестве единственного параметра этой функции следует передать идентификатор выгружаемой раскладки клавиатуры hkl. Переключение раскладки клавиатурыПоследняя функция, которую мы рассмотрим в этом разделе и которая предназначена для работы с раскладками клавиатуры, называется ActivateKeyboardLayout: BOOL ActivateKeyboardLayout( HKL hkl, // идентификатор раскладки клавиатуры UINT Flags); // флаги режима работы функции Эта функция делает текущей раскладку клавиатуры, идентификатор которой передается ей через параметр hkl. Вы можете определить этот идентификатор с помощью функции LoadKeyboardLayout или взять из списка загруженных идентификаторов раскладок, который определяется функцией GetKeyboardLayoutList. Параметр Flags определяет режимы работы функции и имеет следующие значения:
Пример использования функций GetKeyboardLayoutList и ActivateKeyboardLayout вы найдете в исходных текстах приложения SETLOCAL, к описанию которых мы и переходим. Приложение SETLOCALВ приложении SETLOCAL мы демонстрируем использование функций, предназначенных для работы с наборами национальных параметров, для переключения клавиатурных раскладок и получения форматированных текстовых строк времени и даты. Главное окно приложения SETLOCAL показано на рис. 4.1. Рис. 4.1. Главное окно приложения SETLOCAL Если из меню Keyboard выбрать строку Set Layout, на экране появится диалоговая панель Set Keyboard Layout, показанная на рис. 4.2. С помощью этой диалоговой панели вы можете изменить клавиатурную раскладку. Рис. 4.2. Диалоговая панель Set Keyboard Layout С помощью списка Keyboard layouts вы можете выбрать одну из установленных в системе клавиатурных раскладок. Если после выбора раскладки нажать кнопку Set layout или OK, выбранная раскладка станет активной. В поле Enter text to test layout вы можете вводить символы для проверки выбранной раскладки клавиатуры. При этом удобно пользоваться кнопкой Set layout, нажатие на которую не приводит к закрытию диалоговой панели. Кнопка Cancel позволяет отменить изменение текущей раскладки клавиатуры. Если из меню Keyboard выбрать строку Get Layout ID, на экране появится диалоговая панель с идентификатором текущей раскладки клавиатуры. На рис. 4.3 и 4.4 это сообщение показано для американской и русской раскладки клавиатуры. Рис. 4.3. Идентификатор американской раскладки клавиатуры Рис. 4.4. Идентификатор русской раскладки клавиатуры С помощью строк меню Local Info (рис. 4.5) вы можете просмотреть отдельные национальные параметры и изменить текущий набор национальных параметров. Рис. 4.5. Меню Local Info Выбирая строки Set English и Set Russian, вы можете выбирать либо американский, либо русский набор национальных параметров. Заметим, что возможность динамического изменения набора национальных параметров отсутствует в операционной системе Microsoft Windows 95. Там вы можете изменить этот набор только при помощи приложения Control Panel с последующей перезагрузкой компьютера. Для просмотра значений некоторых национальных параметров вы должны выбрать из меню Local Info строку Get Local Info. При этом на экране появится диалоговая панель Set and Get Local Info. На рис. 4.6 и 4.7 мы показали эту диалоговую панель для американского и русского набора параметров, соответственно. Рис. 4.6. Значения некоторых параметров для американского набора национальных параметров Рис. 4.7. Значения некоторых параметров для русского набора национальных параметров Здесь мы отображаем название языка, код страны, номер кодовой страницы OEM и номер кодовой страницы ANSI. Если из меню Local Info выбрать строку Get Date and Time, на экране появится диалоговая панель, отображающая строки даты и времени в формате, соответствующем текущему набору национальных параметров. На рис. 4.8 и 4.9 эти строки показаны для американского и русского набора параметров. Рис. 4.8. Строка даты и времени для американского набора параметров Рис. 4.9 Строка даты и времени для русского набора параметров Исходные тексты приложения SETLOCALГлавный файл исходных текстов приложения SETLOCAL приведен в листинге 4.1. Листинг 4.1. Файл setlocal\setlocal.c // ================================================== // Приложение SETLOCAL // Работа с национальными языками // // (С) Фролов А.В., 1996 // Email: frolov@glas.apc.org // ================================================== #define STRICT #include <windows.h> #include <windowsx.h> #include "resource.h" #include "afxres.h" #include "setlocal.h" HINSTANCE hInst; char szAppName[] = "SetLocalApp"; char szAppTitle[] = "Set and Get Local Info"; // Количество установленных раскладок клавиатуры UINT uLayouts; // Указатель на массив идентификаторов // раскладок клавиатуры HKL * lpList; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; // Сохраняем идентификатор приложения hInst = hInstance; // Проверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если было, выдвигаем окно приложения на // передний план if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Определяем количество установленных // раскалдок клавиатуры uLayouts = GetKeyboardLayoutList(0, NULL); // Заказываем массив для хранения идентификаторов // раскладок клавиатуры lpList = malloc(uLayouts * sizeof(HKL)); // Заполнение массива идентификаторов // раскладок клавиатуры uLayouts = GetKeyboardLayoutList(uLayouts, lpList); // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл // обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Перед завершением работы приложения освобождаем // память, полученную для хранения идентификаторов // раскладок клавиатуры free(lpList); return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { char szBuf[1024]; char szBuf1[1024]; char szKbLayoutName[KL_NAMELENGTH]; BOOL fRc; switch (id) { case ID_FILE_EXIT: { // Завершаем работу приложения PostQuitMessage(0); return 0L; break; } // Установка набора национальных параметров для Англии case ID_LOCALINFO_SETENGLISH: { // Выполнение установки для текущей задачи fRc = SetThreadLocale(MAKELCID( MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT)); // При возникновении ошибки получаем и отображаем ее код if(fRc == FALSE) { wsprintf(szBuf1,"SetThreadLocale: Error %ld\n", GetLastError()); MessageBox(hWnd, szBuf1, "Error", MB_OK); } break; } // Установка набора национальных параметров для России case ID_LOCALINFO_SETRUSSIAN: { fRc = SetThreadLocale(MAKELCID( MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT)); if(fRc == FALSE) { wsprintf(szBuf1,"SetThreadLocale: Error %ld\n", GetLastError()); MessageBox(hWnd, szBuf1, "Error", MB_OK); } break; } // Получение и отображение некоторых // национальных параметров case ID_LOCALINFO_GETLOCALINFO: { // Отображение полного названия национального языка strcpy(szBuf, "LOCALE_SLANGUAGE: "); GetLocaleInfo( GetThreadLocale(), LOCALE_SLANGUAGE, szBuf1, 512); strcat(szBuf, szBuf1); // Отображение кода страны strcat(szBuf, "\nLOCALE_ICOUNTRY: "); GetLocaleInfo( GetThreadLocale(), LOCALE_ICOUNTRY, szBuf1, 512); strcat(szBuf, szBuf1); // Отображение кодовой страницы OEM strcat(szBuf, "\nLOCALE_IDEFAULTCODEPAGE: "); GetLocaleInfo( GetThreadLocale(), LOCALE_IDEFAULTCODEPAGE, szBuf1, 512); strcat(szBuf, szBuf1); // Отображение кодовой страницы ANSI strcat(szBuf, "\nLOCALE_IDEFAULTANSICODEPAGE: "); GetLocaleInfo( GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, szBuf1, 512); strcat(szBuf, szBuf1); MessageBox(hWnd, szBuf, szAppTitle, MB_OK); break; } // Определение и отображение идентификатора // текущей раскладки клавиатуры case ID_KEYBOARD_GETLAYOUTID: { GetKeyboardLayoutName(szKbLayoutName); wsprintf(szBuf1,"Layout ID: %s", szKbLayoutName); MessageBox(hWnd, szBuf1, szAppTitle, MB_OK); break; } // Установка новой раскладки клавиатуры case ID_KEYBOARD_SETLAYOUT: { // Отображение диалоговой панели для выбора // раскладки клавиатуры DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG_SETLAYOUT), hWnd, DlgProc); break; } // Просмотр текущей даты и времени в формате, // принятом для выбранной страны case ID_LOCALINFO_GETDATE: { strcpy(szBuf, "Date: "); // Получаем строку даты GetDateFormat( GetThreadLocale(), LOCALE_NOUSEROVERRIDE | DATE_LONGDATE, NULL, NULL, szBuf1, 512); strcat(szBuf, szBuf1); strcat(szBuf, "\nTime: "); // Получаем строку времени GetTimeFormat( GetThreadLocale(), LOCALE_NOUSEROVERRIDE, NULL, NULL, szBuf1, 512); strcat(szBuf, szBuf1); // Отображаем время и дату MessageBox(hWnd, szBuf, szAppTitle, MB_OK); break; } case ID_HELP_ABOUT: { MessageBox(hWnd, "Set and Get Local Information\n" "(C) Alexandr Frolov, 1996\n" "Email: frolov@glas.apc.org", szAppTitle, MB_OK | MB_ICONINFORMATION); return 0L; break; } default: break; } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } // ----------------------------------------------------- // Функция DlgProc // ----------------------------------------------------- LRESULT WINAPI DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc_OnInitDialog); HANDLE_MSG(hdlg, WM_COMMAND, DlgProc_OnCommand); default: return FALSE; } } // ----------------------------------------------------- // Функция DlgProc_OnInitDialog // ----------------------------------------------------- BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) { UINT i; HKL hklCurrent; char szBuf[256]; // При инициализации диалоговой панели получаем в цикле // идентификаторы установленных раскладок клавиатуры // и заполняем названиями соответствующих национальных // языков список типа COMBOBOX, расположенный в // диалоговой панели for(i=0; i<uLayouts; i++) { // Берем очередной идентификатор раскладки hklCurrent = *(lpList + i); // Получаем название национального языка GetLocaleInfo( MAKELCID(((UINT)hklCurrent & 0xffffffff), SORT_DEFAULT), LOCALE_SLANGUAGE, szBuf, 512); // Вставляем название национального языка в список // типа COMBOBOX SendMessage(GetDlgItem(hdlg, IDC_COMBO1), CB_ADDSTRING, 0, (LPARAM)(LPSTR)szBuf); } return TRUE; } // ----------------------------------------------------- // Функция DlgProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void DlgProc_OnCommand(HWND hdlg, int id, HWND hwndCtl, UINT codeNotify) { // Номер выбранной строки в списке типа COMBOBOX LRESULT uSelectedItem; switch (id) { // Когда пользователь нажимает клавишу OK, // устанавливаем выбранную раскладку клавиатуры // и завершаем работу диалоговой панели case IDOK: { // Определяем номер выбранной в списке строки uSelectedItem = SendMessage( GetDlgItem(hdlg, IDC_COMBO1), CB_GETCURSEL, 0, 0); // Активизируем выбранную раскладку клавиатуры ActivateKeyboardLayout(*(lpList + uSelectedItem), 0); // Завершаем работу диалоговой панели EndDialog(hdlg, 1); return TRUE; } // Когда пользователь нажимает клавишу Set layout, // устанавливаем выбранную раскладку клавиатуры, // не завершая работы диалоговой панели case IDC_BUTTON1: { uSelectedItem = SendMessage( GetDlgItem(hdlg, IDC_COMBO1), CB_GETCURSEL, 0, 0); ActivateKeyboardLayout(*(lpList + uSelectedItem), 0); return TRUE; } // Если пользователь нажал кнопку Cancel, отменяем // смену расклдаки клавиатуры, завершая // работу диалоговой панели case IDCANCEL: { EndDialog(hdlg, 0); return TRUE; } default: break; } return FALSE; } Файл setlocal.h (листинг 4.2) содержит прототипы функций, определенных в приложении SETLOCAL. Листинг 4.2. Файл setlocal\setlocal.h // ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); void WndProc_OnDestroy(HWND hWnd); LRESULT WINAPI DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam); void DlgProc_OnCommand(HWND hdlg, int id, HWND hwndCtl, UINT codeNotify); В файле resource.h (листинг 4.3), который создается автоматически системой разработки приложений Microsoft Visual C++, находятся определения констант для файла описания ресурсов. Листинг 4.3. Файл setlocal\resource.h //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by setlocal.rc // #define IDR_MENU1 101 #define IDR_APPMENU 101 #define IDI_APPICON 102 #define IDI_APPICONSM 103 #define IDD_DIALOG_SETLAYOUT 104 #define IDC_COMBO1 1003 #define IDC_EDIT1 1004 #define IDC_BUTTON1 1005 #define ID_FILE_EXIT 40001 #define ID_HELP_ABOUT 40002 #define ID_KEYBOARD_SETLAYOUT 40004 #define ID_KEYBOARD_GETLAYOUTID 40005 #define ID_LOCALINFO_GETLOCALINFO 40006 #define ID_LOCALINFO_SETENGLISH 40007 #define ID_LOCALINFO_SETRUSSIAN 40008 #define ID_LOCALINFO_GETDATE 40009 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40010 #define _APS_NEXT_CONTROL_VALUE 1006 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif Файл описания ресурсов приложения setlocal.rc приведен в листинге 4.4. В нем определены меню, диалоговая панель, пиктограммы и текстовые строки. Листинг 4.4. Файл setlocal\setlocal.rc //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 ////////////////////////////////////////////////////////////// // // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Keyboard" BEGIN MENUITEM "&Set Layout", ID_KEYBOARD_SETLAYOUT MENUITEM "&Get Layout ID", ID_KEYBOARD_GETLAYOUTID END POPUP "Local Info" BEGIN MENUITEM "&Get Local Info", ID_LOCALINFO_GETLOCALINFO MENUITEM "Get &Date and Time", ID_LOCALINFO_GETDATE MENUITEM SEPARATOR MENUITEM "Set &English", ID_LOCALINFO_SETENGLISH MENUITEM "Set &Russian", ID_LOCALINFO_SETRUSSIAN END POPUP "&Help" BEGIN MENUITEM "&About...", ID_HELP_ABOUT END END #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure // application icon // remains consistent on all systems. IDI_APPICON ICON DISCARDABLE "setlocal.ico" IDI_APPICONSM ICON DISCARDABLE "setlocsm.ico" ////////////////////////////////////////////////////////////// // // Dialog // IDD_DIALOG_SETLAYOUT DIALOG DISCARDABLE 0, 0, 248, 143 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Set Keyboard Layout" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,191,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,191,24,50,14 COMBOBOX IDC_COMBO1,14,65,102,64,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP EDITTEXT IDC_EDIT1,14,28,150,14,ES_AUTOHSCROLL LTEXT "Keyboard layouts:",IDC_STATIC,14,51,57,8 LTEXT "Enter text to test layout:", IDC_STATIC,14,14,75,8 PUSHBUTTON "Set layout",IDC_BUTTON1,191,64,50,14 END ////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_DIALOG_SETLAYOUT, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 241 TOPMARGIN, 7 BOTTOMMARGIN, 136 END END #endif // APSTUDIO_INVOKED #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED Описание функцийПриведем краткое описание наиболее важных функций приложения SETLOCAL. Функция WinMainПомимо выполнения обычных действий, необходимых для создания главного окна приложения, функция WinMain получает список установленных раскладок клавиатуры, сохраняя его в глобальном массиве lpList. Память для этого массива заказывается динамически, поэтому перед завершением работы приложения мы освобождаем эту память явным образом. Для определения размера списка и для получения самого списка раскладок клавиатуры мы используем функцию GetKeyboardLayoutList: UINT uLayouts; HKL * lpList; uLayouts = GetKeyboardLayoutList(0, NULL); lpList = malloc(uLayouts * sizeof(HKL)); uLayouts = GetKeyboardLayoutList(uLayouts, lpList); Функция WndProc_OnCommandФункция WndProc_OnCommand обрабатывает сообщение WM_COMMAND, поступающее в функцию главного окна приложения от меню. Для установки текущего набора национальных символов в этой функции используется описанная нами ранее функция SetThreadLocale, а также макрокоманды MAKELCID и MAKELANGID: fRc = SetThreadLocale(MAKELCID( MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT)); Для получения значений отдельных национальных параметров мы вызываем функцию GetLocaleInfo: GetLocaleInfo(GetThreadLocale(), LOCALE_SLANGUAGE, szBuf1, 512); В качестве идентификатора набора национальных параметров мы указываем идентификатор текущего набора параметров для основной задачи приложения, полученный от функции GetThreadLocale. Имя текущей раскладки клавиатуры определяется при помощи функции GetKeyboardLayoutName: GetKeyboardLayoutName(szKbLayoutName); Для получения форматированной текстовой строки даты и времени мы вызываем функции GetDateFormat и GetTimeFormat: GetDateFormat(GetThreadLocale(), LOCALE_NOUSEROVERRIDE | DATE_LONGDATE, NULL, NULL, szBuf1, 512); GetTimeFormat(GetThreadLocale(), LOCALE_NOUSEROVERRIDE, NULL, NULL, szBuf1, 512); Эта функция выполняет инициализацию списка типа COMBOBOX, расположенного в диалоговой панели выбора новой раскладки клавиатуры. В процессе инициализации в список записываются называния национальных языков, соответствующие установленным раскладкам клавиатуры. Функция DlgProc_OnCommandФункция DlgProc_OnCommand обрабатывает сообщения от органов управления, расположенных в диалоговой панели выбора новой раскладки клавиатуры. После определения выбранной раскладки она активизируется при помощи функции ActivateKeyboardLayout, как это показано ниже: uSelectedItem = SendMessage( GetDlgItem(hdlg, IDC_COMBO1), CB_GETCURSEL, 0, 0); ActivateKeyboardLayout(*(lpList + uSelectedItem), 0); |