Программирование драйверов Windows

Процедура DriverEntry и предварительные объявления


Все приведенные ниже отрывки кода следует последовательно поместить в один файл (обычно, файл, содержащий описание DriverEntry, разработчики называют Init.c). Редактирование, разумеется, удобнее всего выполнять в каком-нибудь редакторе интегрированной среды. Рекомендуется использовать редактор из среды Visual Studio, поскольку в нем производится динамический контроль синтаксиса и типов данных языка С. В главе 2 приводится содержимое файлов настройки проекта для драйвера Example, соблюдение которых позволит воспользоваться динамическими подсказками среды во время редактирования и позволит также выполнять контрольную компиляцию кода. Последнее весьма удобно, поскольку в интегрированной среде легко перейти к месту возникновения ошибки по диагностическому сообщению.

Окончательную компиляцию драйвера (как чистовую, так и отладочную) категорически рекомендуется выполнять утилитой Build из среды DDK, поскольку иные способы компиляции могут быть источником необъяснимых странностей в поведении драйвера.

///////////////////////////////////////////////////////////////////// // init.cpp: Инициализация драйвера // Замечание. Рабочая версия данного драйвера должна быть // скомпилирована как не-WDM версия. В противном случае - драйвер // не сможет корректно загружаться и выгружаться с использованием // программы monitor (пакет Numega Driver Studio) и сервисов SCM // Менеджера.

///////////////////////////////////////////////////////////////////// // DriverEntry Главная точка входа в драйвер // UnloadRoutine Процедура выгрузки драйвера // DeviceControlRoutine Обработчик DeviceIoControl IRP пакетов ///////////////////////////////////////////////////////////////////// #include "Driver.h"

// Предварительные объявления функций: NTSTATUS DeviceControlRoutine( IN PDEVICE_OBJECT fdo, IN PIRP Irp ); VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject); NTSTATUS ReadWrite_IRPhandler( IN PDEVICE_OBJECT fdo, IN PIRP Irp ); NTSTATUS Create_File_IRPprocessing(IN PDEVICE_OBJECT fdo, IN PIRP Irp); NTSTATUS Close_HandleIRPprocessing(IN PDEVICE_OBJECT fdo, IN PIRP Irp);


// Хотя и нехорошо делать глобальные переменные в драйвере... KSPIN_LOCK MySpinLock; #pragma code_seg("INIT") // начало секции INIT ///////////////////////////////////////////////////////////////////// // (Файл init.cpp) // DriverEntry - инициализация драйвера и необходимых объектов // Аргументы: указатель на объект драйвера // раздел реестра (driver service key) в UNICODE // Возвращает: STATUS_Xxx

extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT fdo; UNICODE_STRING devName;

#if DBG DbgPrint("=Example= In DriverEntry."); DbgPrint("=Example= RegistryPath = %ws.", RegistryPath->Buffer); #endif

// Экспорт точек входа в драйвер (AddDevice объявлять не будем) // DriverObject->DriverExtension->AddDevice= OurAddDeviceRoutine; DriverObject->DriverUnload = UnloadRoutine; DriverObject->MajorFunction[IRP_MJ_CREATE]= Create_File_IRPprocessing; DriverObject->MajorFunction[IRP_MJ_CLOSE] = Close_HandleIRPprocessing; DriverObject->MajorFunction[IRP_MJ_READ] = ReadWrite_IRPhandler; DriverObject->MajorFunction[IRP_MJ_WRITE] = ReadWrite_IRPhandler; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]= DeviceControlRoutine; //======================================================== // Действия по созданию символьной ссылки // (их нужно было бы делать в OurAddDeviceRoutine, но у нас // очень простой драйвер и эта процедура отсутствует): RtlInitUnicodeString( &devName, L"\\Device\\EXAMPLE" );

// Создаем наш Functional Device Object (FDO) и получаем // указатель на созданный FDO в нашей переменной fdo. // (В WDM драйвере эту работу также следовало бы выполнять // в процедуре OurAddDeviceRoutine.) При создании FDO // будет выделено место и под структуру расширения устройства // EXAMPLE_DEVICE_EXTENSION (для этого мы передаем в вызов // ее размер, вычисляемый оператором sizeof): status = IoCreateDevice(DriverObject, sizeof(EXAMPLE_DEVICE_EXTENSION), &devName, // может быть и NULL FILE_DEVICE_UNKNOWN, 0, FALSE, // без эксклюзивного доступа &fdo); if(!NT_SUCCESS(status)) return status;



// Получаем указатель на область, предназначенную под // структуру расширение устройства PEXAMPLE_DEVICE_EXTENSION dx = (PEXAMPLE_DEVICE_EXTENSION)fdo->DeviceExtension; dx->fdo = fdo; // Сохраняем обратный указатель

// Применяя прием условной компиляции, вводим функцию DbgPrint, // сообщения которой мы сможем увидеть в окне DebugView, если // выполним сборку нашего драйвера как checked (отладочную) // версию: #if DBG DbgPrint("=Example= FDO %X, DevExt=%X.",fdo,dx); #endif

//======================================= // Действия по созданию символьной ссылки // (их нужно было бы делать в OurAddDeviceRoutine, но у нас // очень простой драйвер): UNICODE_STRING symLinkName; // Сформировать символьное имя: // #define SYM_LINK_NAME L"\\??\\Example" // Такого типа символьные ссылки ^^ проходят только в NT. // (То есть, если перенести бинарный файл драйвера в // Windows 98, то пользовательские приложения заведомо // не смогут открыть файл по такой символьной ссылке.) // Для того, чтобы ссылка работала в и Windows 98 и в NT, // необходимо поступать следующим образом: #define SYM_LINK_NAME L"\\DosDevices\\Example" RtlInitUnicodeString( &symLinkName, SYM_LINK_NAME ); dx->ustrSymLinkName = symLinkName;

// Создаем символьную ссылку status = IoCreateSymbolicLink( &symLinkName, &devName ); if (!NT_SUCCESS(status)) { // при неудаче v удалить Device Object и вернуть управление IoDeleteDevice( fdo ); return status; } // Теперь можно вызывать CreateFile("\\\\.\\Example",...); // в пользовательских приложениях

// Объект спин-блокировки, который будем использовать для // разнесения во времени выполнения кода обработчика // IOCTL запросов. Инициализируем его: KeInitializeSpinLock(&MySpinLock);

// Снова используем условную компиляцию, чтобы выделить код, // компилируемый в отладочной версии и не компилируемый в // версии free (релизной): #if DBG DbgPrint("=Example= DriverEntry successfully completed."); #endif return status; } #pragma code_seg() // end INIT section


Содержание раздела