Клавиатурный ввод в native-приложении

Чтение символов с клавиатуры в native-режиме




Native-приложение, выполняющееся в режиме так называемого «синего экрана», способно читать клавиатурный ввод. Стандартная native-программа autochk.exe, загружаемая для проверки диска после старта компьютера спрашивает пользователя, не отменить ли проверку диска и просит нажать любую клавишу. В случае отмены происходит нормальная загрузка операционной системы без проверки. Чтобы сделать подобное в своём native-приложении, нужно знать, как обрабатывать ввод с клавиатуры.

Ввод текста

Общие принципы чтения с клавиатуры таковы: необходимо открыть устройство клавиатуры как файл, с помощью функции NtCreateFile. Имя файла при этом будет выглядеть как «\Device\KeyboardClass0».

HANDLE hDriver;
UNICODE_STRING Driver;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK Iosb;
RtlInitUnicodeString(&Driver, L"\\Device\\KeyboardClass0");
InitializeObjectAttributes(&ObjectAttributes, &Driver,
	OBJ_CASE_INSENSITIVE, NULL, NULL);
NtCreateFile(&hDriver, SYNCHRONIZE | GENERIC_READ | FILE_READ_ATTRIBUTES,
	&ObjectAttributes, &Iosb, NULL, FILE_ATTRIBUTE_NORMAL,
	0, FILE_OPEN, FILE_DIRECTORY_FILE, NULL,0);

Параллельно нужно создать событие (объект ядра типа Event), которое будет использоваться для ожидания ввода символа с клавиатуры.

InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
Status = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &ObjectAttributes, 1, 0);

Чтение с клавиатуры осуществляется функцией чтения файла NtReadFile, которой в параметрах переданы хэндл клавиатуры и хэндл события.

IO_STATUS_BLOCK Iosb;
LARGE_INTEGER ByteOffset = 0;
NTSTATUS Status;
RtlZeroMemory(&Iosb, sizeof(Iosb));
Status = NtReadFile(hDriver, hEvent, NULL, NULL, &Iosb,
	Buffer, *BufferSize, &ByteOffset, NULL);

Следует проанализировать возвращаемое значение функции и при необходимости подождать наступления события с помощью NtWaitForSingleObject.

if (Status == STATUS_PENDING)
{
	Status = NtWaitForSingleObject(hEvent, TRUE, NULL);
}

NtReadFile вернёт данные в виде структуры KEYBOARD_INPUT_DATA. Эта структура имеет следующий формат:

typedef struct _KEYBOARD_INPUT_DATA {
  USHORT UnitId;
  USHORT MakeCode;
  USHORT Flags;
  USHORT Reserved;
  ULONG  ExtraInformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

Поле MakeCode содержит сканкод нажатой клавиши, а поле Flags — необходимую дополнительную информацию о том, были ли также нажаты одновременно Shift, Ctrl или что-то ещё. Шелл должен содержать таблицу, из которой по сканкоду и флагам можно будет выбрать конкретный символ, соответствующий определённому сочетанию клавиш.

Полученный символ можно возвратить из собственного аналога стандартной функции getch. Из символов можно складывать строки.

Вывод текста

Вывод текста на экран чрезвычайно прост и заключается в помещении в строку типа UNICODE_STRING какого-либо текста, и последующем вызове функции NtDisplayString:

UNICODE_STRING unic;
RtlInitUnicodeString(&unic, L"Hello, world!\n");
NtDisplayString(&unic);

Функция поддерживает два управляющих символа — перевод строки \n и возврат каретки \r. На этой основе строится обработка команд и вывод информации на экран.

Пример использования кода, описанного в данном тексте можете смотреть в исходниках программы Native Shell.



Автор: амдф
Дата: 02.03.2011


При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт.