Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содеожание] [Дальше]

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр.

4 Национальные параметры

  • Наборы национальных параметров
  • Форматное преобразование даты и времени
  • Изменение раскладки клавиатуры
  • Приложение SETLOCAL
  • Операционная система 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. Список констант приведен ниже.

     Константа

     Описание

     LOCALE_SYSTEM_DEFAULT

     Идентификатор, который используется операционной системой по умолчанию

     LOCALE_USER_DEFAULT

     Идентификатор, который используется по умолчанию для текущего пользователя, работающего в среде Microsoft Windows NT

    Макрокоманда 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))
    

    Первичный идентификатор задает национальный язык, а вторичный - его диалект или разновидность.

    Для первичного идентификатора вы можете указать одно из следующих значений:

     Первичный идентификатор

     Национальный язык

     LANG_AFRIKAANS

     Африканский

     LANG_ALBANIAN

     Албанский

     LANG_ARABIC

     Арабский

     LANG_BASQUE

     Баский

     LANG_BULGARIAN

     Болгарский

     LANG_BYELORUSSIAN

     Белорусский

     LANG_CATALAN

     Каталанский

     LANG_CHINESE

     Китайский

     LANG_CROATIAN

     Хорватский

     LANG_CZECH

     Чехословацкий

     LANG_DANISH

     Датский

     LANG_DUTCH

     Нидерландский

     LANG_ENGLISH

     Английский

     LANG_ESTONIAN

     Эстонский

     LANG_FINNISH

     Финнский

     LANG_FRENCH

     Французский

     LANG_GERMAN

     Немецкий

     LANG_GREEK

     Греческий

     LANG_HEBREW

     Еврейский

     LANG_HUNGARIAN

     Венгерский

     LANG_ICELANDIC

     Исландский

     LANG_INDONESIAN

     Индонезийский

     LANG_ITALIAN

     Итальянский

     LANG_JAPANESE

     Японский

     LANG_KOREAN

     Корейский

     LANG_LATVIAN

     Латвийский

     LANG_LITHUANIAN

     Литовский

     LANG_NEUTRAL

     Нейтральный

     LANG_NORWEGIAN

     Норвежский

     LANG_POLISH

     Польский

     LANG_PORTUGUESE

     Португальский

     LANG_ROMANIAN

     Румынский

     LANG_RUSSIAN

     Русский

     LANG_SLOVAK

     Словацкий

     LANG_SLOVENIAN

     Словенский

     LANG_SORBIAN

     Сербский

     LANG_SPANISH

     Испанский

     LANG_SWEDISH

     Шведский

     LANG_THAI

     Таиландский

     LANG_TURKISH

     Турецкий

     LANG_UKRANIAN

     Украинский

    Ниже мы привели список допустимых вторичных идентификаторов:

     Вторичный идентификатор

     Диалект

     SUBLANG_CHINESE_HONGKONG

     Гонконгский диалект китайского

     SUBLANG_CHINESE_SIMPLIFIED

     Упрощенный диалект китайского

     SUBLANG_CHINESE_SINGAPORE

     Сингапурский диалект китайского

     SUBLANG_CHINESE_TRADITIONAL

     Традиционный китайский

     SUBLANG_DEFAULT

     Диалект, который используется по умолчанию

     SUBLANG_DUTCH

     Нидерландский

     SUBLANG_DUTCH_BELGIAN

     Бельгийский диалект нидерландского

     SUBLANG_ENGLISH_AUS

     Австрийский диалект английского

     SUBLANG_ENGLISH_CAN

     Канадский диалект английского

     SUBLANG_ENGLISH_EIRE

     Ирландский диалект английского

     SUBLANG_ENGLISH_NZ

     Новозеландский диалект английского

     SUBLANG_ENGLISH_UK

     Британский диалект английского

     SUBLANG_ENGLISH_US

     Американский диалект английского

     SUBLANG_FRENCH

     Французский

     SUBLANG_FRENCH_BELGIAN

     Бельгийский диалект французского

     SUBLANG_FRENCH_CANADIAN

     Канадский диалект французского

     SUBLANG_FRENCH_SWISS

     Шведский диалект французского

     SUBLANG_GERMAN

     Немецкий

     SUBLANG_GERMAN_AUSTRIAN

     Австрийский диалект немецкого

     SUBLANG_GERMAN_SWISS

     Швейцарский диалект немецкого

     SUBLANG_ITALIAN

     Итальянский

     SUBLANG_ITALIAN_SWISS

     Швейцарский диалект итальянского

     SUBLANG_NEUTRAL

     Нейтральный

     SUBLANG_PORTUGUESE

     Португальский

     SUBLANG_PORTUGUESE_BRAZILIAN

     Бразильский диалект португальского

     SUBLANG_SPANISH

     Испанский

     SUBLANG_SPANISH_MEXICAN

     Мексиканский диалект испанского

     SUBLANG_SPANISH_MODERN

     Современный испанский

     SUBLANG_SYS_DEFAULT

     Диалект, который используется операционной системой по умолчанию

    Заметим, что хотя приложение может указывать любые из перечисленных выше идентификаторов национальных языков, функция SetThreadLocale сможет установить только те, что были выбраны при установке операционной системы Microsoft Windows NT.

    И еще одно замечание.

    Если в качестве первичного идентификатора языка указать константу LANG_NEUTRAL, то комбинации с идентификаторами SUBLANG_NEUTRAL, SUBLANG_DEFAULT и SUBLANG_SYS_DEFAULT будут иметь специальное значение, как это показано ниже:

     Вторичный идентификатор в комбинации с LANG_NEUTRAL

     Национальный язык

     SUBLANG_NEUTRAL

     Нейтральный язык

     SUBLANG_DEFAULT

     Язык, который установлен по умолчанию для текущего пользователя, работающего с Microsoft Windows NT

     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 определяет режимы работы функции. Для этого параметра вы можете указать следующие значения:

     Константа

     Описание

     LOCALE_NOUSEROVERRIDE

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

     TIME_NOMINUTESORSECONDS

     Не использовать минуты или секунды

     TIME_NOSECONDS

     Не использовать секунды

     TIME_NOTIMEMARKER

     Не использовать маркер

     TIME_FORCE24HOURFORMAT

     Всегда использовать 24-часовой формат времени

    Параметр 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. В противном случае строка формата должна быть сформирована приложением.

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

     Символ

     Компонента времени

     h

     Часы без ведущего нуля в 12-часовом формате

     hh

     Часы с ведущим нулем в 12-часовом формате

     H

     Часы без ведущего нуля в 24-часовом формате

     HH

     Часы с ведущим нулем в 24-часовом формате

     m

     Минуты без ведущего нуля

     mm

     Минуты с ведущим нулем

     s

     Секунды без ведущего нуля

     ss

     Секунды с ведущим нулем

     t

     Маркер (такой как A или P)

     tt

     Многосимвольный маркер (такой как AM или PM)

    Параметры 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" :

     Константа

     Описание

     LOCALE_NOUSEROVERRIDE

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

     DATE_SHORTDATE

     Сокращенный формат даты

     DATE_LONGDATE

     Полный формат даты

     DATE_USE_ALT_CALENDAR

     Использование альтернативного календаря, если таковой определен для данного идентификатора набора национальных параметров

    Отличаются также специальные символы, которые можно использовать в строке формата даты:

     Символ

     Компонента даты

     d

     День месяца без ведущего нуля

     dd

     День месяца с ведущим нулем

     ddd

     Трехбуквенное сокращение дня недели

     dddd

     Полное название дня недели

     M

     Номер месяца без ведущего нуля

     MM

     Номер месяца с ведущим нулем

     MMM

     Трехбуквенное сокращение названия месяца

     MMMM

     Полное название месяца

     y

     Двухзначное обозначение года без ведущего нуля (последние две цифры года)

     yy

     Двухзначное обозначение года с ведущим нулем

     yyyy

     Полный номер года

     gg

     Название периода или эры

    Пример использования функции 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 и может иметь следующие значения:

     Константа

     Описание

     KLF_ACTIVATE

     Если указанная раскладка клавиатуры не была загружена ранее, она загружается и становится активной

     KLF_REORDER

     В этом случае раскладка циклически сдвигается в списке загруженных раскладок

     KLF_SUBSTITUTE_OK

     Использование альтернативной раскладки клавиатуры, указанной в регистрационной базе данных (ключ HKEY_CURRENT_USER\Keyboard Layout\Substitutes)

     KLF_UNLOADPREVIOUS

     Используется вместе с флагом KLF_ACTIVATE и только тогда, когда указанная раскладка уже загружена. В этом случае загруженная ранее раскладка клавиатуры выгружается

    Выгрузка раскладки клавиатуры

    Зная идентификатор загруженной ранее расклдаки клавиатуры, вы можете выгрузить эту раскладку из памяти. Для этого следует вызвать функцию UnloadKeyboardLayout:

    
    BOOL UnloadKeyboardLayout(
      HKL  hkl); // идентификатор выгружаемой раскладки
    

    В качестве единственного параметра этой функции следует передать идентификатор выгружаемой раскладки клавиатуры hkl.

    Переключение раскладки клавиатуры

    Последняя функция, которую мы рассмотрим в этом разделе и которая предназначена для работы с раскладками клавиатуры, называется ActivateKeyboardLayout:

    
    BOOL ActivateKeyboardLayout(
      HKL  hkl,    // идентификатор раскладки клавиатуры
      UINT Flags); // флаги режима работы функции
    

    Эта функция делает текущей раскладку клавиатуры, идентификатор которой передается ей через параметр hkl. Вы можете определить этот идентификатор с помощью функции LoadKeyboardLayout или взять из списка загруженных идентификаторов раскладок, который определяется функцией GetKeyboardLayoutList.

    Параметр Flags определяет режимы работы функции и имеет следующие значения:

     Константа

     Описание

     KLF_REORDER

     Система выполняет циклический сдвиг раскладок клавиатур в списке

     KLF_UNLOADPREVIOUS

     Выгрузка раскладки, которая раньше была активна

    Пример использования функций 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);
    
    [Назад] [Содеожание] [Дальше]