Итак, продолжим. Теперь у нас есть готовый драйвер, который мы сделали в прошлой статье. Теперь его надо как-то поиспользовать. Как мы будем это делать? Решим обычную задачу:
С помощью драйвера через пользовательскую программу хотим записывать данные в нужный порт и читать данные из порта под ОС Windows NT, 2000, XP.
Вот этим мы сейчас именно и займемся, но сначала, надо зарегестрировать наш драйвер, поскольку ОС про него пока ничего не знает. Для того чтобы зарегистровать драйвер в системе, надо выполнить следующие шаги:
Теперь создадим пользовательскую программу, которая сама прав доступа к портам ввода-вывода в Windows NT не имеет, но используя наш драйвер Port.sys сможет легко работать с портами компьютера. Итак, создаем пустой проект консольного приложения в VS++, создаем пустой файл *.cpp и копируем туда ниже следующий код:
#include < windows.h>
#include < iostream.h>
#include < stdio.h>
#include < conio.h>
//определяем коды дейсвий для драйвера
#define IOCTL_READ (0x800<<2)|(0x22<<16)
#define IOCTL_WRITE (0x801<<2)|(0x22<<16)
HANDLE hDrv;
DWORD cbRet;
int main()
{
//открываем драйвер
hDrv = CreateFile ( "\\\\.\\MYDRIVER", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
// если не удалось, выходим из программы
if ( hDrv == INVALID_HANDLE_VALUE )
{
cout<<"Error! Can`t open driver. Press any key to exit..."<
return false;
}
else
cout<<"Driver is open!"<
//Тестируем запись данных в порт через драйвер
USHORT DataToDriver[2];
DataToDriver[0]=888; //адрес порта куда писать
DataToDriver[1]=224; //что писать в порт
DeviceIoControl(hDrv,IOCTL_WRITE,DataToDriver,4,NULL,0,&cbRet,NULL);
//Тестируем чтение данных из порта через драйвер
USHORT DataToDriver_[1];
DataToDriver_[0]=888; //адрес порта откуда читать
USHORT DataFromDriver; //куда записать результат чтения
DeviceIoControl(hDrv,IOCTL_READ,DataToDriver_,2,&DataFromDriver,2,&cbRet,NULL);
cout<
return true;
}
Компилируем, запускаем. Если все в порядке: программа откомпилировалась, драйвер установился, то Вы должны увидеть следующее: строчку с сообщением об успешном открытии драйвера, затем в регистре Data LPT порта должно появиться число 224, далее это число отобразиться на экране. (Наблюдать факт появления числа в регистре лучше используя какой-либо блок светодиодов, подключенный к LPT порту.)
"Что же здесь происходит?" - спросите Вы. Давайте разбираться. В самом начале программы, после подключения необходимых заголовочных файлов, идет определение кодов дейсвия драйвера, которые используются приложением для обращения к драйверу для выполнения определенного дейсвия. Они Вам ни чего не напоминают? Да, именно теже коды были заданы нами в прошлой статье в коде драйвере. Теперь используя их, мы можем требовать от драйвера делать то, что ему положено при поступлении того или иного кода дейсвия.
Далее мы получаем доступ к драйверу (налаживаем канал связи) с помощью функции CreateFile(), используя в качестве имени драйвера его специально определенное символическое имя MYDRIVER в пространстве имен NT. Если результат открытия был неудачным (например, драйвер не установился), то мы улавливаем этот момент и выходим из программы, т.к. делать нам тогда нечего.
Теперь начанается самое интересное - обращения к драйверу. Немного теории. Для общения с драйвером существует специальная функция DeviceIoControl(), принимающая целую кучу парпаметров. Ниже представлены пронуменрованные параметры этой функции:
Сначала проводится тестирование записи данных в порт. Для этого создается массив DataToDriver из 2-х элементов. В первый элемент массива помещается адрес порта, куда необходимо записать данные. В этом примере используется адрес 888 - адрес регистра Data LPT порта в десятичной форме. Во второй элемент размещаем данные, которые надо переслать в порт. В данном случае это число 224. Потом идет вызов упомянутой выше функции DeviceIoControl(), которой мы передаем следующие параметры:
Далее идет тестирование чтения данных из порта. Создаем массив из одного элемента (можно было просто переменную) DataToDriver_. В единственный элемент этого массива записываем адрес порта откуда нужно читать данные. В данном случае это все тотже регистр Data LPT порта. Определяем переменную DataFromDriver, в которую поместим результат чтения. Опять вызываем DeviceIoControl(), передавая ей параметры:
После последнего вызова функции DeviceIoControl() в переменной DataFromDriver будет находиться число, прочитанное из порта. Его мы именно в конце программы и выводим на экран.
Ну вот и все. Теперь Вы вполне можете сами написать простой драйвер и использовать его в своих целях. На этом статьи по драйверам не заканчиваются; в следующей статье мы поговорим про динамическую загрузку драйвера, что является гораздо более удобным способом установки драйвера нежели используя реестр с дальнейшей перезагрузкой компьютера.