В начале, как полагается, разберемся с вопросом: зачем нам нужны виртуальные и скан-коды клавиш?
Лично для меня изучить этот вопрос сподвигла небольшая проблема при тестировании одной из программ в отделе экологов. Дело в том, что встречается такой тип людей, которые почему-то страх как боятся компьютеров, несмотря на то, что без них не могут обойтись, да к тому же не могут толком объяснить, в случае проблемы, что собственно случилось.
Начинаешь тестировать программу, устанавливаешь на компьютер пользователя, объясняешь все популярно (тот, естественно, что-то помечает на обрывках бумажек), спрашиваешь «Понял?», тот — «Да, понял.» Уходишь, через день приходишь и начинается «Программа не работает», «Там что-то какое-то окошко появилось» и т.д. и т.п. Спрашиваешь «Что делал?» Показывает действие за действием — ошибок нет…Как работал человек? Не понятно. Сидеть целый день за спиной у него и наблюдать — не вариант. Заставлять записывать за собой всё, что делал — изуверство. Интерфейс упростил дальше некуда, даже не 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-форме и при желании вы их сможете легко расшифровать. Если возникнут трудности — обращайтесь, разберемся вместе.
Здравствуйте, Влад. Возможно мой вопрос вам покажется странным и не по адресу, но на просторах интернета я себе внятного ответа не обнаружил.
Суть в следующем: требуется написать программу, одной из задач которой является выполнение процедуры по нажатию по F5 и CTRL+F5, а так же перекодировка символов в теле программы. Не могли бы вы мне подсказать, как это можно реализовать? желательно без использования ДЛЛ, на выходе предпочтительно иметь 1 экзешник.
Алексей, я бы зарегистрировал пару сочетаний горячих клавиш при запуске программы и «прятал» бы такую программку, например, в трей. По поводу регистрации горячих клавиш надо смотреть инфу по методам: RegisterHotKey() и UnregisterHotKey(). Получается программка без DLL не мазолящая глаза :)
Если еще актуально… Горячие клавиши удобно отлавливать с помощью ActionList. Создаешь Action в свойству ShortCut присваиваешь сочетание клавиш. А на OnExecute нужную процедуру обработки.
xkeen, Такие комментарии всегда актуальны, тем более в блоге связанном с программированием — посетители узнают ещё один способ работы с горячими клавишами. Спасибо :)
Извините за повтор в первом сообщение не указал адрес блога. Скажите пожалуйста возможно реализовать немного иную программу, а именно : перехват нажатия на виртуальной клавиатуре и транслировать их во все приложения как нажатие обычной клавиатуры?
[…] скан коды клавиш delphi […]
Здравствуйте, очень хорошая и полезная программа, в последней редакции она определяет буквенное значение нажатой клавиши, а нет ли у вас скомпилированного первого варианта программы которая выдает сканкоды клавиш, я не программист, попытался скомпилировать сам — не получилось , пожалуйста выложите готовлю программу, заранее благодарен!
Я все откомпилировал в RAD Studio XE5 Если надо пришлю. Пиши мне на oleg.guzz@gmail.com
Разгружу автора, помогу хоть этим. Я сейчас ломаю голову над переводом кодов в символы. Зачет автору. Многое нашел в сети, но чем то не подходили. Тут же отлавливается любая клавиша. Да и как то бы избавиться от записи в текст из длл-ки — это я про следующую тему.
AssignFile(ft, ‘c:keyhook.txt’);
c:keyhook.txt это для развития мозгов слеша не хватает и еще собак кое где:)