Валерий Борисок - Delphi. Трюки и эффекты
3.2. Клавиатура
Клавиатура является основным средством для ввода информации в компьютер, поэтому не будем обходить стороной и рассмотрим некоторые не так часто используемые или не такие очевидные возможности работы с ней.
Определение информации о клавиатуре
Начнем с небольшого примера, позволяющего определить некоторую информацию о клавиатуре (листинг 3.16). Пример основан на использовании API-функции GetKeyboardType.
...Листинг 3.16.
Определение информации о клавиатуре
procedure TForm1.FormCreate(Sender: TObject);
begin
//Определяем тип клавиатуры
case GetKeyboardType(0) of
1: txtType.Text := 'PC/XT или совместимая (83 клавиши)
2: txtType.Text := 'Olivetti «ICO» (102 клавиши)
3: txtType.Text := 'PC/AT (84 клавиши) или похожая
4: txtType.Text := 'Расширенная (101 или 102 клавиши)
5: txtType.Text := 'Nokia 1050 или похожая
6: txtType.Text := 'Nokia 9140 или похожая
7: txtType.Text := 'японская
end;
//Определяем код типа производителя
txtSubtype.Text := IntToStr(GetKeyboardType(1));
//Определяем количество функциональных клавиш
txtKeys.Text := IntToStr(GetKeyboardType(2));
end;
При создании формы происходит заполнение текстовых полей информацией о типе клавиатуры, коде типа, присвоенном производителем, и количестве функциональных клавиш.
Пример возможного результата определения информации о клавиатуре приводится на рис. 3.2.
Рис. 3.2. Информация о клавиатуре
Опрос клавиатуры
Существует достаточно удобная альтернатива обработке событий клавиатурного ввода, которая может оказаться особенно полезной, когда нужно знать состояние сразу нескольких клавиш.
В листинге 3.17 приводится пример обработчика события TimerlTimer, определяющего, нажаты ли клавиши ↑, ↓, ←, →, а также пробел, Enter, Ctrl (правый) и Alt (правый).
...Листинг 3.17.
Определение состояния некоторых клавиш
procedure TForm1.Timer1Timer(Sender: TObject);
var
buttons: TKeyBoardstate;
begin
//Получаем состояния клавиш
GetKeyboardState(buttons);
//Отобразим состояния клавиш
//..пробел
if buttons[VK_SPACE] and 128 <> 0 then
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..enter
if buttons[VK_RETURN] and 128 <> 0 then
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..правый Ctrl
if buttons[VK_RCONTROL] and 128 <> 0 then
SendMessage(cmbRCtrl.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbRCtrl.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..правый Alt
if buttons[VK_RMENU] and 128 <> 0 then
SendMessage(cmbRAlt.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbRAlt.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..правый Shift
if buttons[VK_RSHIFT] and 128 <> 0 then
SendMessage(cmbRShift.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbRShift.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..вверх
if buttons[VK_UP] and 128 <> 0 then
SendMessage(cmbUp.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbUp.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..вниз
if buttons[VK_Down] and 128 <> 0 then
SendMessage(cmbDown.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbDown.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..влево
if buttons[VK_LEFT] and 128 <> 0 then
SendMessage(cmbLeft.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbLeft.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..вправо
if buttons[VK_RIGHT] and 128 <> 0 then
SendMessage(cmbRight.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbRight.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
end;
Для того чтобы определить состояние клавиш, можно использовать API-функцию GetKeyboardState, которая заполняет массив buttons (на самом деле тип TKeyBoardstate определен как array [0. 255] of Byte) значениями, характеризующими, нажата ли клавиша. Причем значения в массиве buttons трактуются следующим образом:
• если установлен старший бит (проверка чего и делается в листинге 3.17), то клавиша в данный момент нажата;
• если установлен младший бит, то функция, закрепленная за этой клавишей (например, Caps Lock), включена.
Для индексации массива можно использовать ASCII-коды символов, а также константы, соответствующие несимвольным клавишам (обозначения и коды для таких клавиш приводятся в приложении 1).
Каждой контролируемой клавише (листинг 3.17) соответствует кнопка на форме. Для принудительной установки кнопки в нажатое или ненажатое состояние используется посылка сообщения BMSETSTATE. Пример определения состояния клавиш в некоторый момент времени показан на рис. 3.3.
Рис. 3.3. Состояние некоторых клавиш клавиатуры
Интересно, что рассмотренный способ работы с клавиатурой можно использовать даже для определения неисправных клавиш на клавиатуре, например, как это сделано в одной из программ пакета Norton Utilities.
Имитация нажатия клавиш
Состояние клавиш на клавиатуре можно не только определять, его также можно программно изменять. Рассмотрим один из способов программного нажатия клавиш, который крайне прост благодаря наличию API-функции keybdevent, как раз и предназначенной для имитации клавиатурного ввода.
Назначения параметров этой функции поясним на примере (листинг 3.18)....Листинг 3.18.
Показываем меню Пуск
procedure TForm1.cmbStartClick(Sender: TObject);
begin
//Имитируем нажатие клавиши Windows
keybd_event(VK_LWIN, 0, 0, 0);
//Имитируем отпускание клавиши Windows
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);
end;
Нас интересуют, прежде всего, первый и третий параметры функции keybdevent (второй не используется, а третий предназначен для установки дополнительной информации, относящейся к нажатию клавиши). Первым параметром функции передается код «нажимаемой» или «отпускаемой» клавиши. Третий параметр равен нулю при «нажатии» и константе KEYEVENTF_KEYUP при «отпускании» клавиши.
...Внимание!
При использовании keybd_event главное – не забывать «отпускать» программно нажатые клавиши (как это делается в приведенных здесь примерах). Иначе есть риск изрядных «г люков» клавиатурного ввода.
Аналогичный приведенному в листинге 3.18 пример программного нажатия клавиши Print Screen (снятия копии экрана) приводится в листинге 3.19.
...Листинг 3.19.
Снятие копии экрана
procedure TForm1.cmbPrintScreenClick(Sender: TObject);
begin
//Нажимаем Print Screen
keybd_event(VK_SNAPSHOT, 0, 0, 0);
keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0);
end;
В листинге 3.20 приводится пример нажатия комбинации из нескольких клавиш (Windows+M для сворачивания всех окон).
...Листинг 3.20.
Сворачивание всех окон
procedure TForm1.cmbMinimizeAllClick(Sender: TObject);
begin
//Нажимаем Windows+M
keybd_event(VK_LWIN, 0, 0, 0);
keybd_event(Byte('M'), 0, 0, 0);
keybd_event(Byte('M'), 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);
end;
Добавление к этой комбинации клавиши Shift приведет к восстановлению первоначального состояния окон.
Последний пример иллюстрирует, как можно использовать программное нажатие клавиш для ускорения быстрого доступа к программам. Имеется в виду программное нажатие сочетаний клавиш, ассоциированных с ярлыками, расположенными на Рабочем столе или находящимися в меню Пуск. Допустим, на компьютере используется сочетание клавиш Ctrl+Alt+E для запуска Internet Explorer. Пример программного нажатия этой комбинации клавиш приведен в листинге 3.21.
...Листинг 3.21.
Быстрый запуск программы
procedure TForm1.cmbEIxplorerClick(Sender: TObject);
begin
//Нажимаем комбинацию Ctrl+Alt+E
keybd_event(VK_CONTROL, 0, 0, 0);
keybd_event(VK_MENU, 0, 0, 0);
keybd_event(Byte('E'), 0, 0, 0);
keybd_event(Byte('E'), 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
end;
Последний пример особенно полезен для запуска сразу нескольких программ (для этого ярлыкам этих программ должны быть назначены сочетания клавиш).
«Бегущие огни» на клавиатуре
В завершение рассмотрим довольно забавный пример, так же, как и предыдущий, основанный на программном нажатии клавиш Caps Lock, Num Lock и ScroLL Lock. Как известно, этим клавишам соответствуют три лампочки (по крайней мере, на большинстве клавиатур). Суть примера состоит в последовательном включении/выключении упомянутых клавиш, которое автоматически сопровождается включением/выключением соответствующих лампочек.
Перед рассмотрением основных процедур примера приведем текст процедуры PressKey, которая далее используется практически на каждом шагу (листинг 3.22). Она имитирует нажатие одной клавиши с переданным кодом.
...Листинг 3.22.
Нажатие одной клавиши
procedure PressKey(keyCode: Byte);
begin
keybd_event(keyCode, 0, 0, 0 );
keybd_event(keyCode, 0, KEYEVENTF_KEYUP, 0 );
end;
Запуск и остановка огней осуществляется при нажатии кнопки (помимо кнопки, на форме должно быть текстовое поле, в котором вводится интервал между сменой состояния огней, а также таймер со свойством Enabled, равным False) (листинг 3.23).
...Листинг 3.23.
Запуск и остановка огней
var
initCaps, initNum, initScroll: Boolean; //Первоначальные
//состояния клавиш
curCaps, curNum, curScroll: Boolean; //Текущие состояния
//клавиш
procedure TForm1.cmbStartClick(Sender: TObject);
begin
if cmbStart.Caption = 'Старт' then
begin
//Сохраняем первоначальные состояния клавиш
initCaps := (GetKeyState(VK_CAPITAL) and 1) <> 0;
initNum := (GetKeyState(VK_NUMLOCK) and 1) <> 0;
initScroll := (GetKeyState(VK_SCROLL) and 1) <> 0;
//Включаем только Caps Lock
if not initCaps then PressKey(VK_CAPITAL);