Win32 API. Работа с клавиатурой
ОГЛАВЛЕНИЕ
О вводе информации с клавиатуры
Все прикладные программы, написанные для Microsoft Windows должны принимать вводимую пользователем информацию с клавиатуры также как и от мыши. Базирующаяся на Windows программа, принимает ввод информации с клавиатуры в форме сообщений, посылаемых в ее окна.
Модель ввода информации с клавиатуры
Windows обеспечивает аппаратно - независимую поддержку клавиатуры для прикладных программ, при помощи установки драйвера устройства, соответствующего текущей клавиатуре. Windows предоставляет независимую от языка поддержку клавиатуры, при помощи использования специфической для языка раскладки символов на клавиатуре, в настоящее время выбранную пользователем или прикладной программой. Драйвер устройства клавиатуры принимает от клавиатуры скэн-коды, которые передаются в соответствии с раскладкой символов на ней, где они преобразуются в сообщения, и посылаются в соответствующие окна в вашей прикладной программе.
Присвоенное для каждой клавиши на клавиатуре уникальное значение, называемое скэн-кодом (scan code), аппаратно-зависимый идентификатор для клавиши на клавиатуре. Клавиатура генерирует два скэн-кода, когда пользователь манипулирует клавишей - первый, когда пользователь нажимает клавишу, а второй, когда пользователь отпускает клавишу.
Драйвер устройства клавиатуры интерпретирует скэн-код и переводит его в код виртуальной клавиши (virtual-key code), в не зависящее от устройства значение, определяемое Windows, которое идентифицирует целевую клавишу. После трансляции скэн-кода, раскладка символов на клавиатуре создает сообщение, которое включает в себя скэн-код, код виртуальной клавиши и другую информацию о нажатии клавиши, а затем помещает сообщение в системную очередь сообщений. Windows удаляет сообщение из системной очереди сообщений и посылает его в очередь сообщений соответствующего потока. В конечном счете, цикл сообщения потока удаляет сообщение и посылает его соответствующей оконной процедуре для обработки.
Фокус и активизация клавиатуры
Windows посылает сообщения клавиатуры в очередь сообщений активного потока, который создал окно с фокусом клавиатуры. Фокус клавиатуры (keyboard focus) - временное свойство окна. Windows дает возможность совместно использовать клавиатуру всем окнам на экране, при помощи перемещения фокуса клавиатуры по указанию пользователя из одного окна в другое. Окно, которое имеет фокус клавиатуры, принимает (из очереди сообщений потока, который создал его) все сообщения клавиатуры, пока фокус не перейдет к другому окну.
Поток может вызывать функцию GetFocus, чтобы определить, которое из его окон (если есть в наличии) в настоящее время имеет фокус клавиатуры. Поток может дать фокус клавиатуры одному из своих окон, при помощи вызова функции SetFocus. Когда фокус клавиатуры меняет одно окно на другое, система отправляет сообщение WM_KILLFOCUS окну, которое потеряет фокус, а затем отправляет сообщение WM_SETFOCUS окну, которое получает фокус.
Концепция фокуса клавиатуры связывается с понятием активного окна. Активное окно (active window) - окно верхнего уровня, с которым пользователь в настоящее время работает. Окно с фокусом клавиатуры является или активным окном, или дочерним окном активного окна. Таким образом, чтобы пользователь мог легко идентифицировать активное окно, система размещает его сверху в Z-последовательности и делает выделенными область его заголовка (если оно является единственным) и рамку.
Пользователь может активизировать окно верхнего уровня, щелкнув по нему мышью, выбрать его, используя комбинацию клавиш ALT+TAB или ALT+ESC, или выбрать его, если оно в Списке Задач (Task List). Поток может активизировать окно верхнего уровня, используя функцию SetActiveWindow. Он может определить, является ли окно верхнего уровня, которое он создал активным, используя функцию GetActiveWindow.
Когда одно окно деактивизируется, а другое активизируется, Windows сначала отправляет сообщение WM_ACTIVATE деактивизируемому, а затем активизируемому окну. Младшее слово параметра wParam равно нулю, если окно деактивизируется, и отличается от нуля, если оно активизируется. Когда заданная по умолчанию оконная процедура принимает сообщение WM_ACTIVATE, она устанавливает фокус клавиатуры в активном окне.
Сообщения о нажатии клавиши
Нажатие клавиши заканчивается сообщением WM_KEYDOWN или WM_SYSKEYDOWN, помещаемое в очередь сообщений потока, связанного с окном, которое имеет фокус клавиатуры. Отпуск клавиши заканчивается сообщением WM_KEYUP или WM_SYSKEYUP, помещаемым в очередь.
Действия "клавиша отпущена" и "клавиша нажата" посылают сообщения, которые обычно происходят попарно, но если пользователь удерживает нажатой клавишу достаточно долго, чтобы запустить функцию клавиатуры автоматического повторения, система генерирует ряд сообщений WM_KEYDOWN или WM_SYSKEYDOWN. Она затем генерирует единственное сообщение WM_KEYUP или WM_SYSKEYUP, когда пользователь отпускает клавишу.
Системные и несистемные нажатия клавиш
Windows делает различие между системными и несистемными нажатиями клавиш. Системные нажатия клавиш производят системные сообщения WM_SYSKEYDOWN и WM_SYSKEYUP. Несистемные нажатия клавиш производят несистемные сообщения WM_KEYDOWN и WM_KEYUP.
Если ваша оконная процедура должна обработать сообщение системного нажатия клавиши, удостоверьтесь, что после обработки сообщения процедура посылает его в функцию DefWindowProc. Иначе, все системные действия, включающие клавишу ALT, будут заблокированы всякий раз, когда окно получает фокус клавиатуры. То есть пользователь не будет получать возможности обратиться к меню окна или Системному меню, или использовать комбинацию клавиш ALT+ESC или ALT+TAB, чтобы активизировать другое окно.
Сообщения о системном нажатии клавиши используются главным образом операционной системой Windows, а не прикладной программой. Windows использует их, чтобы предоставить свой встроенный интерфейс клавиатуры для меню и дать возможность пользователю управлять окном, которое является активным. Сообщения о системном нажатии клавиши создаются тогда, когда пользователь использует клавишу в комбинации с клавишей ALT, или когда пользователь вводит с клавиатуры, а ни у какого окна нет фокуса клавиатуры (например, когда активная прикладная программа свернута). В этом случае, сообщения посылаются в очередь сообщений, связанную с активным окном.
Сообщение о несистемном нажатии клавиши предназначены для использования окнами прикладной программы; функция DefWindowProc с ними не делает ничего. Оконная процедура может сбрасывать любые несистемные сообщения нажатия клавиши, которые ей не нужны.
Описание кодов виртуальной клавиши
Параметр wParam сообщения нажатия клавиши содержит код виртуальной клавиши, которая была нажата или отпущена. Оконная процедура обрабатывает или игнорирует сообщение о нажатии клавиши, что зависит от значения кода виртуальной клавиши.
Типичная оконная процедура обрабатывает только небольшое подмножество сообщений нажатия клавиши, которые она принимает и игнорирует остальные. Например, оконная процедура может обрабатывать только сообщения нажатия клавиши WM_KEYDOWN, и только те, которые содержат коды виртуальных клавиш для клавиш управления курсором, клавиши SHIFT (также называемой клавишей управления) и функциональных клавиш. Типичная оконная процедура не обрабатывает сообщения нажатия клавиши от символьных клавиш. Вместо этого, она использует функцию TranslateMessage, чтобы конвертировать сообщение в символьные сообщения. Для получения дополнительной информации о TranslateMessage и символьных сообщениях, см. Символьные сообщения.
Флажки сообщения о нажатии клавиши
Параметр lParam сообщения нажатия клавиши содержит дополнительную информацию о нажатии клавиши, которая генерировала сообщение. Эта информация включает в себя счет повторений, скэн-код, флажок дополнительной клавиши, код контекста, флажок предыдущего состояния клавиши и флажок переходного состояния.
Прикладная программа может использовать следующие значения, чтобы управлять флажками нажатия клавиши:
- KF_ALTDOWN - Управляет флажком клавиши ALT, который указывает, нажата ли клавиша ALT.
- KF_DLGMODE - Управляет флажком режима диалога, который указывает, является ли диалоговое окно активным.
- KF_EXTENDED - Управляет флажком дополнительной клавиши.
- KF_MENUMODE - Управляет флажком режима меню, который указывает, является ли меню активным.
- KF_REPEAT - Управляет счетчиком повторений.
- KF_UP - Управляет флажком переходного состояния.
Счетчик повторений
Вы можете проверить счетчик повторений, чтобы определить, представляет ли сообщение нажатия клавиши больше чем одно нажатие. Система увеличивает счетчик, когда клавиатура генерирует сообщения WM_KEYDOWN или WM_SYSKEYDOWN быстрее, чем прикладная программа может обрабатывать их. Это часто происходит тогда, когда пользователь удерживает нажатой клавишу достаточно долго, чтобы запустить функцию автоматического повторения клавиатуры. Вместо заполнения системной очереди сообщениями, происходящими в результате нажатия клавиши, система объединяет сообщения в единственное сообщение нажатой клавиши и увеличивает счет повторений. Отпуск клавиши не может запустить функцию автоматического повторения, так что счет повторений для сообщений WM_KEYUP и WM_SYSKEYUP всегда устанавливается в 1.
Скэн-код
Скэн-код - значение, которое аппаратные средства клавиатуры генерируют, когда пользователь нажимает клавишу. Это - аппаратно-зависимое значение, которое идентифицирует нажатую клавишу, в противоположность символу, представленному клавишей. Прикладная программа обычно игнорирует скэн-коды. Вместо этого, она использует не зависящие от устройства коды виртуальной клавиши, чтобы правильно понять сообщения нажатий клавиши.
Флажок дополнительной клавиши
Флажок дополнительной клавиши указывает, произошло ли сообщение нажатия клавиши от одной из дополнительных клавиш на расширенной клавиатуре. Дополнительные клавиши состоят из клавиш ALT и CTRL в правой части клавиатуры; INS, DEL, HOME, END, PAGE UP, PAGE DOWN и клавиши курсора в группах слева от цифровой клавиатуры; клавиша NUM LOCK; клавиша BREAK (CTRL+PAUSE); клавиша PRINT SCRN; делитель (/) и клавиши ENTER в цифровой клавиатуре. Флажок дополнительной клавиши устанавливается, если клавиша является дополнительной клавишей.
Контекстный Код
Контекстный код указывает, была ли клавиша ALT нажата тогда, когда было сгенерировано сообщение о нажатии клавиши. Если клавиша ALT была нажата, код будет 1 и 0, если этого не было.
Флажок предыдущего состояния клавиши
Флажок предыдущего состояния клавиши указывает, была ли клавиша, которая генерировала сообщение о нажатии, перед этим нажата или отпущена. Он будет 1, если клавиша в предыдущем состоянии была нажата и 0, если клавиша в предыдущем состоянии была отпущена. Вы можете использовать этот флажок, чтобы идентифицировать сообщения нажатия клавиши, сгенерированные автоматической функцией повторения клавиатуры. Этот флажок установлен в 1 для сообщений WM_KEYDOWN и WM_SYSKEYDOWN нажатия клавиши, сгенерированных автоматической функцией повторения. Он всегда устанавливается в 0 для сообщений WM_SYSKEYUP и WM_KEYUP.
Флажок переходного состояния
Флажок переходного состояния указывает, что генерировало сообщение нажатия клавиши - либо нажатие клавиши или отпуск её. Этот флажок всегда устанавливается в 0 для сообщений WM_SYSKEYDOWN и WM_KEYDOWN; он всегда устанавливается в 1 для сообщений WM_SYSKEYUP и WM_KEYUP.
Символьные сообщения
Сообщения о нажатии клавиши предоставляют много информации о нажатой клавише, но они не предусматривают кодов букв при нажатии символьных клавиш. Чтобы получить данные о кодах букв, прикладная программа должна включить функцию TranslateMessage в цикл сообщений своего потока. Функция TranslateMessage посылает сообщение WM_KEYDOWN или WM_SYSKEYDOWN раскладке символов на клавиатуре. Раскладка проверяет код виртуальной клавиши сообщения и, если он соответствует клавише знака, предоставляет эквивалент кода буквы (принимая во внимание состояние клавиш SHIFT и CAPS LOCK). Функция затем генерирует символьное сообщение, которое включает в себя код буквы и размещает сообщение сверху очереди сообщений. Следующий повтор цикла сообщения удаляет символьное сообщение из очереди и отсылает сообщение соответствующей оконной процедуре.
Несистемные символьные сообщения
Оконная процедура может принимать четыре различных символьных сообщения, включая WM_CHAR, WM_DEADCHAR, WM_SYSCHAR и WM_SYSDEADCHAR. Функция TranslateMessage генерирует сообщение WM_CHAR или WM_DEADCHAR тогда, когда она обрабатывает сообщение WM_KEYDOWN. Точно так же она генерирует сообщение WM_SYSCHAR или WM_SYSDEADCHAR тогда, когда обрабатывает сообщение WM_SYSKEYDOWN.
Прикладная программа, которая обрабатывает ввод информации с клавиатуры обычно, игнорирует все, кроме сообщения WM_CHAR, посылая любые другие сообщения функции DefWindowProc. Windows использует сообщения WM_SYSCHAR и WM_SYSDEADCHAR, чтобы осуществлять мнемонику меню.
Параметр wParam всех символьных сообщений содержит код буквы клавиши, которая была нажата. Значение кода буквы зависит от класса окна, принимающего сообщение. Если функция RegisterClass была использована, чтобы зарегистрировать класс окна в версии Уникода, система предоставляет символы Unicode всем окнам этого класса. И напротив, система предоставляет коды букв ASCII из набора символов Windows. Для получения дополнительной информации об Уникоде, см. Уникод и наборы символов.
Содержание параметра lParam символьного сообщения идентично содержанию параметра lParam сообщения о нажатии клавиши, которое было оттранслировано, чтобы произвести символьное сообщение. За информацией о содержании параметра lParam, см. Флажки сообщения о нажатии клавиши.
Сообщения о диакритическом знаке
Некоторые неанглийские клавиатуры содержат знаковые клавиши, которые не требуют создания своих символов. Вместо этого, они используются, чтобы добавить диакритический знак к символу, произведенному последующим нажатием клавиши. Эти клавиши - называются "мертвыми" клавишами (dead keys). Клавиша диакритического знака (A) над гласной (который по-другому называется циркумфлекс) в немецкой клавиатуре - пример "мертвой" клавиши. Чтобы ввести символ, состоящий из "o" с циркумфлексом, немецкий пользователь нажимает клавишу циркумфлекса, сопровождаемую нажатием клавиши "o". Окно с фокусом клавиатуры принимает следующую последовательность сообщений:
WM_KEYDOWN
WM_DEADCHAR
WM_KEYUP
WM_KEYDOWN
WM_CHAR
WM_KEYUP
Функция TranslateMessage генерирует сообщение WM_DEADCHAR тогда, когда она обрабатывает сообщение WM_KEYDOWN от "мертвой" клавиши. Хотя параметр wParam сообщения WM_DEADCHAR содержит код буквы диакритического знака для мертвой клавиши, прикладная программа обычно игнорирует это сообщение. Вместо этого она обрабатывает сообщение WM_CHAR, сгенерированное последующим нажатием клавиши. Параметр wParam сообщения WM_CHAR содержит код буквы символа с диакритическим знаком. Если последующее нажатие клавиши генерирует символ, который не может быть объединен с диакритическим знаком, Windows генерирует два сообщения WM_CHAR. Параметр wParam первого содержит код буквы диакритического знака; параметр wParam второго содержит код буквы последующей знаковой клавиши.
Функция TranslateMessage генерирует сообщение WM_SYSDEADCHAR тогда, когда она обрабатывает сообщение WM_SYSKEYDOWN от системной "мертвой" клавиши ("мертвая" клавиша, которая нажата в комбинации с клавишей ALT). Прикладная программа обычно игнорирует сообщение WM_SYSDEADCHAR.
Состояние клавиши
В ходе обработки сообщения клавиатуры, у прикладной программы возникает необходимость установить состояние другой клавиши кроме той, для которой сгенерировано текущее сообщение. Например, прикладная программа обработки текстов (текстовый процессор), которая позволяет пользователю нажимать SHIFT+END, чтобы выбрать блок текста, должна проверять состояние клавиши SHIFT всякий раз, когда она принимает сообщение о нажатии от клавиши END. Прикладная программа может использовать функцию GetKeyState, чтобы установить, было ли сгенерировано состояние виртуальной клавиши во время текущего сообщения; она может использовать функцию GetAsyncKeyState, чтобы получить данные о текущем состоянии виртуальной клавиши.
Раскладка символов на клавиатуре сохраняет список названий. Название клавиши, которая создает отдельный символ то же самое, что и символ, созданный клавишей. Название не символьной клавиши типа TAB и ENTER сохраняется как строка символов. Прикладная программа может извлекать данные о названии любой клавиши из драйвера устройства путем вызова функции GetKeyNameText.
Трансляции нажатия клавиши и символьной клавиши
Windows включает в себя несколько специальных функций, назначение которых, перевод скэн-кодов, кодов букв и кодов виртуальной клавиши при помощи предоставления различных сообщений о нажатии клавиши. Эти функции включают в себя MapVirtualKey, ToAscii, ToUnicode и VkKeyScan.
Поддержка "горячей" клавиши
Windows предоставляет набор функций, которые прикладные программы могут использовать, чтобы определить "горячую" клавишу. "Горячая" клавиша (hot key) - комбинация клавиш, которая генерирует сообщение WM_HOTKEY, сообщение, которое система помещает сверху очереди сообщений потока, обходя любые существующие сообщения в очереди. Прикладные программы используют "горячую" клавишу, чтобы получить первоочередной ввод информации с клавиатуры от пользователя. Например, определяя "горячую" клавишу, состоящую из комбинации клавиш CTRL+C, прикладная программа может дать возможность пользователю отменить затянувшуюся операцию.
Чтобы определить "горячую" клавишу, прикладная программа вызывает функцию RegisterHotKey, устанавливающую комбинацию клавиш, которая генерирует сообщение WM_HOTKEYWM_HOTKEY помещается в очередь сообщений потока, который создал данное окно. Параметр wParam сообщения содержит идентификатор "горячей" клавиши. Прикладная программа может определять многочисленные комбинации "горячая" клавиша для потока, но каждая "горячая" клавиша в потоке должна иметь уникальный идентификатор. Прежде, чем прикладная программа завершит свою работу, она должна использовать функцию UnregisterHotKey, чтобы уничтожить комбинацию "горячая" клавиша.
Прикладные программы могут использовать управление "горячей" клавишей, чтобы сделать себя легкодоступными для пользователя, который выбирает комбинацию "горячая клавиша". Элементы управления комбинацией "горячая" клавиша обычно используются, чтобы определить "горячую" клавишу, которая активизирует окно; они не используют функции RegisterHotKey и UnregisterHotKey. Вместо этого, прикладная программа, которая использует орган управления комбинацией "горячая" клавиша обычно, передает сообщение WM_SETHOTKEY, чтобы установить "горячую" клавишу. Всякий раз, когда пользователь нажимает "горячую" клавишу, система отправляет сообщение WM_SYSCOMMAND, устанавливающее SC_HOTKEY. Для получения дополнительной информации об элементах управления комбинацией "горячая клавиша", см. Органы управления "горячей " клавишей.
Языки, регионы и раскладки символов на клавиатуре
Язык (language) - естественный язык, такой как английский, французский и японский. Подмножество языка (sublanguange) - вариант естественного языка, на котором говорят в некоторых географических районах, типа английских подмножеств языка, на которых говорят в Великобритании и Соединенных Штатах. Базирующиеся на Win32 прикладные программы используют значения, называемые регионами (locales), чтобы уникально идентифицировать языки и их подмножества. Прикладные программы обычно используют регионы, чтобы установить язык, на котором обрабатывать ввод и вывод данных. Настройка региона для клавиатуры, например, воздействует на символьные значения, сгенерированные клавиатурой. Настройка региона для дисплея или принтера воздействует на глифы (образы символа в побитовом отображении), показанные на экране или напечатанные. Прикладные программы устанавливают регион для клавиатуры, загружая и используя раскладки символов на клавиатуре. Они устанавливают регион для дисплея или принтера, выбирая шрифт, который поддерживает данный географический район.
Раскладка символов на клавиатуре определяет не только физическую позицию клавиш на ней, но также обуславливает и символьные значения, создаваемые, при помощи нажатий этих клавиш. Каждая раскладка связана с регионом, который идентифицирует текущий язык ввода информации и обуславливает, какие символьные значения создавать при помощи клавиш и их комбинаций.
Каждая раскладка символов на клавиатуре имеет соответствующий дескриптор, который идентифицирует раскладку и язык. Младшее слово дескриптора - идентификатор языка. Старшее слово - дескриптор устройства, определяющего физическую раскладку, или равно нулю, указывая на заданную по умолчанию физическую раскладку. Пользователь может объединить любой язык ввода информации с данной физической раскладкой. Например, англоязычный пользователь, который очень редко работает по-французски, может установить язык ввода клавиатуры французский без изменения физической раскладки клавиатуры. Это означает, что пользователь может вводить текст на французском языке, используя знакомую английскую раскладку клавиатуры.
Обычно не предполагается, что прикладные программы будут непосредственно управлять языками ввода. Вместо этого, пользователь устанавливает комбинацию языка и раскладки, а затем переключается среди них. Когда пользователь щелкает мышью по тексту, имеющему знаки из другого языка, прикладная программа вызывает функцию ActivateKeyboardLayout, чтобы активизировать заданную по умолчанию раскладку пользователя для этого языка. Если пользователь редактирует текст на языке, которого нет в действующем списке, прикладная программа может вызывать функцию LoadKeyboardLayout с языком, чтобы получить раскладку базирующуюся на этом языке.
Функция ActivateKeyboardLayout устанавливает язык ввода для текущей задачи. Её параметр hkl может быть или дескриптор раскладки символов на клавиатуре или дополненный нулями идентификатор языка. Дескрипторы раскладки символов на клавиатуре могут быть получены из функции LoadKeyboardLayout или GetKeyboardLayoutList. Значения HKL_NEXT и HKL_PREV могут также быть использованы, чтобы выбрать следующую или предыдущую клавиатуру. Для некоторых операционных систем, значение KLF_UNLOADPREVIOUS не имеет никакого смысла и игнорируется.
Функция GetKeyboardLayoutName извлекает данные о названии активной раскладки символов на клавиатуре для вызывающего потока. Если прикладная программа создает активную раскладку, используя функцию LoadKeyboardLayout, GetKeyboardLayoutName извлекает ту же самую строку, которую использовали, чтобы создать раскладку. Иначе, эта строка - первичный идентификатор языка, соответствующий региону активной раскладки. Это означает, что функция не обязательно может различать между собой различные раскладки с тем же самым первичным языком, так как не может возвратить конкретную информацию о языке ввода. Тем не менее, функция GetKeyboardLayout может быть использована, чтобы установить язык ввода информации.
Функция LoadKeyboardLayout загружает раскладку символов на клавиатуре и делает её доступной пользователю. Прикладные программы могут делать раскладку активной непосредственно для текущего потока, используя значение KLF_ACTIVATE. Прикладная программа может использовать значение KLF_REORDER, чтобы переупорядочить раскладки без такой установки значения KLF_ACTIVATE. Прикладные программы должны всегда использовать значение KLF_SUBSTITUTE_OK при загрузке раскладок символов на клавиатуре, чтобы гарантировать, что предпочтение пользователя, если таковое вообще имеются, обеспечено. Для некоторых операционных систем, значение KLF_UNLOADPREVIOUS игнорируется.
Для многоязычной поддержки, функция LoadKeyboardLayout предоставляет флажки KLF_NOTELLSHELL и KLF_REPLACELANG. Флажок KLF_REPLACELANG предписывает функции поменять существующую раскладку символов на клавиатуре без изменения языка. Попытка заменить существующую раскладку, используя тот же самый идентификатор языка, но без установки KLF_REPLACELANG, является ошибкой. Флажок KLF_NOTELLSHELL ограждает функцию от уведомления оболочки, когда раскладка символов на клавиатуре добавлена или заменена. Это полезно для прикладных программ, которые прибавляют многочисленные раскладки в ряде последующих обращений. Этот флажок должен быть использоваться у всех, но при последнем вызове.
Функция UnloadKeyboardLayout ограничена в том, что она не может выгрузить из системы язык ввода по умолчанию. Это гарантирует то, что пользователь всегда имеет одну раскладку, доступную для ввода текста, которая использует тот же самый набор символов, какой используется оболочкой и файловой системой.
Использование ввода информации с клавиатуры
Окно принимает ввод информации с клавиатуры в форме сообщений о нажатии клавиши и символьных сообщений. Цикл сообщений, связанный с окном должен включать в себя код, который транслирует сообщения о нажатии клавиши в соответствующие символьные сообщения. Если окно в своей рабочей области отображает ввод информации с клавиатуры, оно должно создать и показать каретку, чтобы обозначить позицию, куда будет введен следующий символ. Следующие разделы описывают код, который содержит в себе прием, обработку и отображение ввода информации с клавиатуры.
Обработка сообщений о нажатии клавиши
Оконная процедура окна, которое имеет фокус клавиатуры, принимает сообщения о нажатии клавиши, когда пользователь печатает на клавиатуре. Сообщения о нажатии клавиши - WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN и WM_SYSKEYUP. Типичная оконная процедура игнорирует все сообщения о нажатии клавиши кроме WM_KEYDOWN. Windows посылает сообщение WM_KEYDOWN, когда пользователь нажимает клавишу.
Когда оконная процедура принимает сообщение WM_KEYDOWN, она должна проверить код виртуальной клавиши, который сопровождает сообщение, чтобы установить, как обработать нажатие клавиши. Код виртуальной клавиши находится в параметре wParam сообщения. Как правило, приложение обрабатывает только нажатия клавиши, сгенерированные не символьными клавишами, включая функциональные клавиши, клавиши управления курсором, и клавиши специального назначения типа INS, DEL, HOME и END.
Следующий пример показывает общую схему оконной процедуры, которую использует типичная прикладная программа, чтобы принимать и обрабатывать сообщения о нажатии клавиши.
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
.
. /* Обрабатывает клавишу LEFT ARROW (Стрелка влево). */
.
break;
case VK_RIGHT:
.
. /* Обрабатывает клавишу RIGHT ARROW (Стрелка вправо). */
.
break;
case VK_UP:
.
. /* Обрабатывает клавишу UP ARROW (Стрелка вверх). */
.
break;
case VK_DOWN:
.
. /* Обрабатывает клавишу DOWN ARROW (Стрелка вниз). */
.
break;
case VK_HOME:
.
. /* Обрабатывает клавишу HOME. */
.
break;
case VK_END:
.
. /* Обрабатывает клавишу END. */
.
break;
case VK_INSERT:
.
. /* Обрабатывает клавишу INS. */
.
break;
case VK_DELETE:
.
. /* Обрабатывает клавишу DEL. */
.
break;
case VK_F2:
.
. /* Обрабатывает клавишу F2. */
.
break;
.
. /* Обрабатывает другие не символьные нажатия клавиш. */
.
default:
break;
}
Трансляция символьных сообщений
Любой поток, который принимает символьный ввод информации от пользователя, должен включать в свой цикл сообщений функцию TranslateMessage. Эта функция проверяет в сообщении о нажатии клавиши ее виртуальный код и, если код соответствует символу, помещает символьное сообщение в очередь сообщений. Символьное сообщение удаляется и отправляется при следующем повторении цикла сообщения; параметр wParam сообщения содержит код буквы.
Вообще, цикл сообщения потока должен использовать функцию TranslateMessage, чтобы транслировать каждое сообщение, а не только сообщения виртуальных клавиш. Хотя TranslateMessage не имеет никакого влияния на другие типы сообщений, она гарантирует, что ввод информации с клавиатуры оттранслируется правильно. Следующий пример показывает, как включить функцию TranslateMessage в типичный цикл сообщений потока.
while (GetMessage(&msg, (HWND) NULL, 0, 0))
{
if (TranslateAccelerator(hwndMain, haccl, &msg) == 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Обработка символьных сообщений
Оконная процедура принимает символьное сообщение тогда, когда функция TranslateMessage транслирует код виртуальной клавиши соответствующий клавише знака. Символьными сообщениями являются WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, и WM_SYSDEADCHAR. Типичная оконная процедура игнорирует все символьные сообщения кроме WM_CHAR. Функция TranslateMessage генерирует сообщение WM_CHAR тогда, когда пользователь нажимает какую-либо из следующих клавиш.
Любую символьную клавишу
BACKSPACE
ENTER (перевод каретки)
ESC
SHIFT+ENTER (перевод строки)
TAB
Когда оконная процедура принимает сообщение WM_CHAR, она должна проверить код буквы, который сопровождает сообщение, чтобы установить, как обрабатывать символ. Код буквы находится в параметре wParam сообщения.
Следующий пример показывает общую схему оконной процедуры, где типичная прикладная программа используется для приема и обработки символьных сообщений.
case WM_CHAR:
switch (wParam)
{
case 0x08:
.
. /* Обработка BACKSPACE . */
.
break;
case 0x0A:
.
. /* Обработка перевода строки. */
.
break;
case 0x1B:
.
. /* Обработка Esc. */
.
break;
case 0x09:
.
. /* Обработка табуляции TAB. */
.
break;
case 0x0D:
.
. /* Обработка перевода каретки. */
.
break;
default:
.
. /* Обработка воспроизводимых символов. */
.
break;
}
Использование каретки
Окно, которое принимает ввод информации с клавиатуры, обычно отображает символы, которые пользователь вводит с клавиатуры в рабочей области окна. Окно должно использовать каретку, чтобы указать позицию в рабочей области, где появится следующий символ. Окно должно также создавать и показывать на экране каретку, когда оно принимает фокус клавиатуры и скрывать и уничтожать каретку, когда оно теряет фокус. Окно может исполнять эти операции в ходе обработки сообщений WM_KILLFOCUS и WM_SETFOCUS. Для получения дополнительной информации о каретках, см. главу Каретки.
Отображение ввода информации с клавиатуры
Пример в этом разделе показывает, как прикладная программа может принимать символы от клавиатуры, отображать их в рабочей области окна и обновлять позицию каретки с каждым напечатанным символом. Он также демонстрирует, и как переместить каретку в ответ на нажатие клавиш LEFT ARROW (стрелка влево), RIGHT ARROW (стрелка вправо), HOME (вверх документа) и END (в конец документа), и показывает, как выделить выбранный текст в ответ на нажатие комбинации клавиш SHIFT+RIGHT ARROW.
В ходе обработки сообщения WM_CREATE, оконная процедура, показанная в примере, назначает буфер 64КБ для сохранения ввода информации с клавиатуры. Она также извлекает данные о метрике в настоящее время загруженного шрифта, сохраняя высоту и среднюю ширину символов в шрифте. Высота и ширина используются при обработке сообщения WM_SIZE, чтобы вычислить длину строки и максимальное число строк, основываясь на размере рабочей области.
Оконная процедура создает и отображает на экране каретку при обработке сообщения WM_SETFOCUS. Она скрывает и удаляет каретку при обработке сообщения WM_KILLFOCUS.
При обработке сообщения WM_CHAR, оконная процедура отображает символы, сохраненные ею в буфере вводимой информации, и обновляет позицию каретки. Оконная процедура также преобразует и символы табуляции в четыре последовательных пробела. Возврат на один символ, перевод строки и знаки перехода генерируют гудок, а иначе, не обрабатываются.
Оконная процедура исполняет движения каретки влево, вправо, в конец и в исходную позицию при обработке сообщения WM_KEYDOWN. При обработке действия клавиши RIGHT ARROW (стрелки "вправо"), оконная процедура проверяет состояние клавиши SHIFT и, если она нажата, выбирает символ справа от каретки, поскольку каретка перемещается.
Обратите внимание! на то, что нижеследующий код написан так, чтобы его можно было откомпилировать или как Уникод ™ или как ANSI. Если исходный текст определяет Уникод, строки обрабатываются как символы Уникода; иначе, они обрабатываются как символы ANSI.
#define BUFSIZE 65535
#define SHIFTED 0x8000
LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
UINT wParam;
LONG lParam;
{
HDC hdc; /* дескриптор контекста устройства */
TEXTMETRIC tm; /* структура для текстовой метрики */
static DWORD dwCharX; /* средняя ширина символа */
static DWORD dwCharY; /* высота символа */
static DWORD dwClientX; /* ширина рабочей области */
static DWORD dwClientY; /* высота рабочей области */
static DWORD dwLineLen; /* длина строки */
static DWORD dwLines; /* строки текста в рабочей области */
static int nCaretPosX = 0; /* горизонтальная позиция каретки */
static int nCaretPosY = 0; /* вертикальная позиция каретки */
static int nCharWidth = 0; /* ширина символа */
static int cch = 0; /* символы в буфере */
static int nCurChar = 0; /* индекс текущего символа */
static PTCHAR pchInputBuf; /* адрес вводного буфера */
int i, j; /* цикл счета */
int cCR = 0; /* счетчик переводов каретки */
int nCRIndex = 0; /* индекс последнего перевода каретки */
int nVirtKey; /* код виртуальной клавиши */
TCHAR szBuf[128]; /* временный буфер */
TCHAR ch; /* текущий символ */
PAINTSTRUCT ps; /* требуется для BeginPaint */
RECT rc; /* прямоугольник вывода для DrawText */
SIZE sz; /* размеры строк */
COLORREF crPrevText; /* предыдущий цвет текста */
COLORREF crPrevBk; /* предыдущий цвет фона */
switch (uMsg) {
case WM_CREATE:
/* Получим метрику текущего шрифта. */
hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);
/* Сохраним среднюю ширину и высоту символа. */
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
/* Разместим в памяти буфер для ввода информации с клавиатуры. */
pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));
return 0;
case WM_SIZE:
/* Сохраним новую ширину и высоту рабочей области. */
dwClientX = LOWORD(lParam);
dwClientY = HIWORD(lParam);
/* Вычислим максимальную ширину строки и максимальное
* число строк в рабочей области. */
dwLineLen = dwClientX - dwCharX;
dwLines = dwClientY / dwCharY;
break;
case WM_SETFOCUS:
/*
* Создадим, позиционируем и отобразим каретку в окне,
* которое приняло фокус ввода.
*/
CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
ShowCaret(hwndMain);
break;
case WM_KILLFOCUS:
/*
* Скроем и разрушим каретку, когда окно теряет фокус ввода с клавиатуры.
*/
HideCaret(hwndMain);
DestroyCaret();
break;
case WM_CHAR:
switch (wParam) {
case 0x08: /* на символ назад */
case 0x0A: /* перевод строки */
case 0x1B: /* переход */
MessageBeep(0xFFFFFFFF);
return 0;
case 0x09: /* табуляция */
/* Преобразование табуляции в четыре последовательных пробела. */
for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;
case 0x0D: /* возврат каретки */
/*
* Запись перевода каретки и позиции каретки в начале новой строки.
*/
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default: /* отображаемый символ */
ch = (TCHAR) wParam;
HideCaret(hwndMain);
/*
* Извлечение данных о ширине символа и вывод символа.
*/
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam, &nCharWidth);
TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY, &ch, 1);
ReleaseDC(hwndMain, hdc);
/* сохранение символа в буфере. */
pchInputBuf[cch++] = ch;
/*
* Вычисление новой горизонтальной позиции каретки.
* Если позиция превышает максимум, вставьте перевод каретки,
* и переместите каретку в начало следующей строки.
*/
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen) {
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwndMain);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT: /* клавиша LEFT ARROW */
/*
* Каретка может переместится только в начало текущей строки.
*/
if (nCaretPosX < 0) {
HideCaret(hwndMain);
/*
* Извлекает данные о символе слева от каретки,
* вычисляет ширину символа затем вычитает ширину
* из текущей горизонтальной позиции каретки, чтобы
* получить новую позицию.
*/
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth,0);
ShowCaret(hwndMain);
}
break;
case VK_RIGHT: /* Клавиша RIGHT ARROW */
/*
* Каретка перемещается вправо или когда
* определяется перевод каретки, чтобы начать
* следующую строку.
*/
if (nCurChar > cch) {
HideCaret(hwndMain);
/*
* Извлекаем данные о символе справа от каретки.
* Если они являются переводом каретки, каретка
* позиционируется в начале следующей строки.
*/
ch = pchInputBuf[nCurChar];
if (ch == 0x0D) {
nCaretPosX = 0;
nCaretPosY++;
}
/*
* Если символ не является переводом каретки, то
* проверяем, не нажата ли клавиша SHIFT. Если она
* нажата. Инвертируем цвет текста и выводим символ.
*/
else {
hdc = GetDC(hwndMain);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED) {
crPrevText = SetTextColor(hdc,RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,RGB(0,0,0));
TextOut(hdc, nCaretPosX,nCaretPosY * dwCharY,&ch,1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
/*
* Получаем ширину символа и вычисляем новую
* горизонтальную позицию каретки.
*/
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwndMain);
break;
}
break;
case VK_UP: /* клавиша UP ARROW (стрелка вверх) */
case VK_DOWN: /* клавиша DOWN ARROW (стрелка вниз)*/
MessageBeep(0xFFFFFFFF);
return 0;
case VK_HOME: /*клавиша HOME */
*
* Устанавливаем позицию каретки в верхнем левом
* углу рабочей области.
*/
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END: /* клавиша END */
/* Перемещаем каретку в конец текста. */
for (i=0; i < cch; i++) {
/*
* Считаем число переводов каретки и сохраняем
* индекс последнего счета.
*/
if (pchInputBuf[i] == 0x0D) {
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
/*
* Копируем весь текст между последним переводом
* каретки и концом из буфера ввода с клавиатуры
* во временный буфер.
*/
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
/*
* Извлекаем данные о протяженности текста и используем
* их, чтобы установить горизонтальную позицию каретки.
*/
hdc = GetDC(hwndMain);
GetTextExtentPoint32(hdc, szBuf, lstrlen(szBuf),&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwndMain, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_PAINT:
if (cch == 0) /* в вводном буфере нет ничего */
break;
hdc = BeginPaint(hwndMain, &ps);
HideCaret(hwndMain);
/*
* Устанавливаем прямоугольник отсечения, а затем
* рисуем текст внутри него.
*/
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);
ShowCaret(hwndMain);
EndPaint(hwndMain, &ps);
break;
.
. /* Обрабатываем другие сообщения. */
.
case WM_DESTROY:
PostQuitMessage(0);
/* Освобождаем буфер ввода. */
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwndMain, 0xAAAA);
break;
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}