Смекни!
smekni.com

Переопределение назначений клавиш на клавиатуре в операционной системе Windows (стр. 3 из 5)

Существует два типа драйверов фильтров: драйвер-фильтр верхнего и нижнего уровня. В рамкой данной задачи не имеет значения на каком этапе будет производится переопределение. Но предпочтительнее выбрать драйвер-фильтр верхнего уровня, поскольку информация возвращаемая драйвером клавиатуры хорошо документирована и описана в литературе.

Ввиду того, что все современные драйверы, рекомендуется писать согласно стандарту PnP, поскольку они обладают большей функциональностью, то разрабатываемый драйвер должен быть драйвером PnP.

Формализация постановки задачи:

- Необходимо написать драйвер-фильтр верхнего уровня для драйвера клавиатуры.

- Драйвер-фильтр должен перехватывать IRP пакеты, содержащие скэнкоды нажатых и отпущенных клавиш, переопределять, если необходимо, скэнкоды на скэнкоды других клавиш. Он должен удалять записи, соответствующие отключенным клавишам.

- Решение о том, какие клавиши должны быть переопределены или отключены, принимается в соответствии со списком замен, который хранится в памяти ядра.

- Для установки списка замен используется пользовательское приложение, которое пересылает в драйвер список замен.

- Драйвер должен быть драйвером PnP.

- Драйвер-фильтр не должен тормозить ввод с клавиатуры и работу всей системы в целом. Драйвер-фильтр должен обеспечить надежную работу системы.

2. Конструкторский раздел

2.1 Точки входа разрабатываемого драйвера-фильтра

Поскольку разрабатываемый драйвер-фильтр является драйвером PnP, то должен иметь следующие точки входа:

- DriverEntry

- DriverUnload

- AddDevice

- Функции для обработки пакетов IRP

Функции для обработки прерываний в данной работе не регистрируются, поскольку драйвер не работает с прерываниями.

DiverEntry

В данной работе процедура DriverEntry выполняет следующие действия:

· Заполнение массива MajorFunctions. Регистрируется процедура обработки пакета на чтение, процедура обработки IOCTL запросов, процедуры обработки запросов от менеджера PnP и менеджера питания. Остальные элементы массива заполняются адресом функции MyPassNext, которая пропускает пакеты ниже по стеку.

· Регистрация процедуры AddDevice. В данной работе она называется MyAddDevice.

· Регистрация процедуры DriverUnload, называющейся MyUnload.

· Выделение памяти для хранения массива замен клавиш. Одна запись массива занимает 4 байта, а максимум может быть только 103 замены (клавиша Pause/Break не в счет). Значит максимальный объем массива равен 412 байт. DriverEntry сразу выделяет при загрузке эти 412 байт. Не имеет смысла экономить и выделять память динамически при каждой инициализации массива, поскольку 412 – это очень мало, и система не будет тратить время на освобождение и выделение памяти при каждой инициализации.

· Инициализация некоторых глобальных переменных: AltPressed, CtrlPressed, KeyPause.

DriverEntry регистрирует только необходимые процедуры. Поскольку проект представляет собой драйвер-фильтр верхнего уровня, и в нем нет необходимости обрабатывать прерывания, то не производится регистрация DriverStartIo, процедур ISR и DPC.

AddDevice

В данной работе функция MyAddDevice создает одно функциональное устройство с именем \Device\MyFilter. При создании устройства происходит резервирования места для хранения адреса устройства, расположенного ниже в стеке драйверов. Это сделано для того, чтобы при разрушении стека драйверов передать запрос PnP на демонтаж нижестоящему драйверу. Созданное устройство подключается к стеку драйверов клавиатуры. Это делается с помощью функции IoAttachDeviceToDeviceStack. Это стандартная функция Windows, она принимает PDO и указатель на структуру подключаемого FDO. FDO занимает место в стеке драйверов сразу после объекта, находящегося в вершине стека. Теперь подключаемый FDO становится вершиной стека. Нельзя подключится к стеку когда он уже сформирован, поэтому необходимо подключится к нему в определенный момент. Очередность загрузки драйверов описана в реестре Windows. Программа установки производит необходимую регистрацию. Структура этой программы описана ниже.

Для того чтобы пользовательское приложение смогло обратиться к драйверу (для загрузки в драйвер списка замен или для получения списка замен, которые драйвер осуществляет в данный момент) для FDO должно быть зарегистрировано DOS имя. Используя это имя приложение сможет послать драйверу IOCTL запрос. Для регистрации такого имени создается строка юникод со значением \DosDevices\MyFilter и применяется функция IoCreateSymbolicLink. Ее параметрами является только что созданная строка и имя FDO, которое обслуживает наш драйвер. Теперь \DosDevices\MyFilter – это DOS имя созданного FDO устройства.

DriverUnload

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

Процедуры обработки пакетов IRP

Разрабатываемый драйвер-фильтр осуществляет обработку следующих пакетов IRP:

- IRP_MJ_DEVICE_CONTROL

- IRP_MJ_READ

- IRP_MJ_PNP

Остальные IRP пакеты пропускаются ниже по стеку драйверов.

Функция обработки пакетов IRP_MJ_DEVICE_CONTROL

В данной работе пользовательское приложение должно иметь возможность посылать IOCTLзапросы драйверу. Приложение должно иметь возможность получить список текущих замен осуществляемых драйвером и передать драйверу новый список замен.

Для этого в теле драйвера определены две 32‑битные константы.

- GetKeys

CTL_CODE (FILE_DEVICE_KEYBOARD, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)

- SetKeys

CTL_CODE (FILE_DEVICE_KEYBOARD, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS)

Это коды IOCTL запросов не использующиеся драйверами стека клавиатуры. Поэтому в данном проекте они могут быть использованы безо всяких опасений. Запросы с первым кодом используется для получения списка текущих замен, со вторым для его установки.

Поскольку в первом случае драйверу необходимо получить буфер с данными, а во втором передать его, то необходимо использовать один из четырех способов передачи данных. В проекте применяется способ METHOD_BUFFERED. Поскольку список замен занимает всего 500 байт, то его размер не повредит системному пулу, и копироваться пользовательский буфер в системный будет очень быстро. Нет необходимости применять более сложные методы METHOD_IN_DIRECT или METHOD_NEITHER использующиеся при передаче больших объемов данных.

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

Функция обработки пакетов IRP_MJ_READ

Данная функция осуществляет обработку пакетов на чтение. IRPпакет сначала будет попадать в разрабатываемый драйвер. Вызовется зарегистрированная в DriverEntry функция MyRead. К моменту вызова MyRead, буфер не содержит кодов считанных клавиш. Для того чтобы получить доступ к ним. MyRead должна установить CallBack процедуру. Она получит управление когда буфер IRP пакета будет содержать информацию о нажатых клавишах и будет подниматься вверх по стеку драйверов и будет вызывать CallBack функции на каждом уровне стека. CallBackпроцедура устанавливается с помощью функции IoSetCompletionRoutine. Далее в MyRead происходит копирование текущей ячейки IRP пакета в следующую ячейку, таким образом. Таким образом происходит передача неизмененных параметров в Kbdclass.

Функция обработки пакетов IRP_MJ_PNP

Драйвер-фильтр должен обрабатывать только запрос IRP_MN_REMOVE_DEVICE. При этом функция посылает данный пакет менеджера PnP нижестоящему в стеке устройству. Затем она производит необходимые завершающие действия:

- отключает устройство от стека драйверов вызовом функции IoDetachDevice

- удаляет устройство FDO вызовом функции IoDeleteDevice

- удаляет символьную ссылку вызовом IoDeleteSymbolicLink

Остальные пакеты пропускаются ниже по стеку.

Обработка остальных пактов IRP

Остальные пакета IRP, которые за ненадобностью не обрабатываются в данном фильтре, пропускаются ниже по стеку. Процедуры данного фильтра не в праве самостоятельно обрабатывать эти запросы, так как это могут запросы, адресованные нижестоящим драйверам. Примером одного из таких запросов является IOCTL запрос, адресованный драйверу i8042prt и предназначенный для перепрограммирования котроллера клавиатуры и для зажжения лампочек на клавиатуре. Такие запросы посылает Windows при обнаружении нажатия CapsLock, NumLock или ScrollLock.

В данной работе за пропускание пакетов вниз отвечает процедура MyPassNext. Она передает IRPпакет нижестоящему драйверу с помощью функции IoCallDriver. При этом нижестоящий драйвер должен считывать текущую ячейку IRP пакета. Это достигается за счет использования функции IoSkipCurrentIrpStackLocation

2.2 Взаимодействие компонентов системы

2.3 Размещение драйвера в памяти

Некоторые процедуры драйвера, те которые выполняют инициализацию, выгодно выполнить и освободить память после выполнения. Поскольку процедуры инициализации выполняются всего один раз при загрузке системы, а после этого находятся в памяти, занимая ценное место. В языке C есть специальная директива, позволяющая разместить инициализирующий код в специальной секции. Память из под этой секции будет возвращена системе после выполнения. Это директива #pragmaalloc_text («INIT», имя). Параметром директивы является имя функции.