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. Список констант приведен ниже.

Константа Описание
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. Из-за ограниченного объема книги мы не имеем возможности все их перечислить, поэтому ограничимся только самыми интересными:

Идентификатор национального языка (длиной не более 5 символов)

Полное название национального языка

Полное английское название языка

Сокращенное трехсимвольное название языка

Естественное названия языка

Код страны (длиной не более 6 символов)

Полное локализованное название страны

Полное английское название страны

Сокращенное название страны

Естественное название страны

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

Основной код страны

Номер кодовой страницы OEM

Номер кодовой страницы ANSI

Символ, который используется для разделения элементов списка

Система измерений (0 - метрическая, 1 - американская)

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

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

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

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

Порядок, в котором располагаются компоненты даты:

0: Месяц-День-Год,

1: День-Месяц-Год,

2: Год-Месяц-День

Естественное длинное название для понедельника

Естественное длинное название для дней недели от вторника до воскресения

Естественное сокращенное название для понедельника

Естественное сокращенное название для дней недели от вторника до воскресения

Естественное длинное название для января

Естественное длинное название для месяцев от февраля до декабря

Помимо перечисленных, предусмотрены многочисленные константы для определения формата отображения даты, времени и денежных единиц, положительных и отрицательных чисел и так далее.

В качестве примера использования функции 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);