Новая библиотека WDF

Опишу-ка я свои приключения с сабжем. WDF расшифровывается Windows driver foundation. В этот пакет входят две разных библиотеки: чисто Си-шная библиотека KMDF для драйверов уровня ядра и UMDF для драйверов, работающих в юзерспейсе, таких как USB и Firewire, основанная на языке Си++ (KMDF -- kernel-mode driver framework, UMDF -- user-mode driver framework). Для меня самое интересное -- это, конечно, KMDF, потому что он позволяет заменить WDM. Исключение -- Win98, на ней новая библиотека не работает.

Как следует из названия, KMDF является фреймворком ("скелет"), то есть специальной средой исполнения драйвера. Программист пишет функции на Си и подключает их к фреймворку, который автоматически вызывает их в нужные моменты. Например, функция DeviceAdd вызывается при обнаружении устройства, или функция FileCreate, которая вызывается в ответ на открытие или создание файла. Это очень похоже на обработку сообщений в WDM, но с одним важным отличием: в WDF есть работающие функции-затычки ко всем системным сообщениям, поэтому не нужно писать код для всего. Чистый WDM в этом отношении проигрывает -- по выражению с habrahabr.ru это софт, созданный в лабораториях абвера по личному указанию фюрера, драйверы в нем имеют пугающие размеры, так как должны обработать все на свете возможности.

С другой стороны, так как это надстройка над WDM, многие недостатки, свойственные ей, перекочевали в новую библиотеку. Например, глупая схема приоритетов прерываний. Из-за требований «кросс-платформенности» (которую никто никогда у Windows не видел), нельзя напрямую работать с PIC, включать и отключать отдельные прерывания, можно только управлять системным уровнем IRQL. Из-за этого многие важные функции недоступны при запрещенных прерываниях. Скажем, нельзя вызывать функцию KeSetSemaphore из процедуры обработки прерываний. И многие другие тоже нельзя вызвать, и на практике проявляется этот запрет в абсолютно непонятных и трудноотлавливаемых ошибках.

Также непонятно, зачем вообще нужны Inf-файлы. Синтаксис у них запутанный, а пользы ноль. Ведь обходится же Линукс без них! А здесь нужно скрупулезно прописывать все детали, не имея почти никакой помощи при отладке, кроме лог-файла c:\windows\setupapi.log.

Отладка драйвера тоже затруднена, потому что отладчик WinDbg.exe отличается специфическим неудобным набором команд. Но это меньшее из зол, связанных с разработкой драйверов под NT. Эффект второй системы, как ни крути.


Начинать лучше всего с примера samples\kmdf\toaster\functional, другие примеры перегружены ненужными вещами. Проекты компилируются командой build -ecgZ, список файлов она читает в sources.

Если компилировать в Release, то компиляция вылетает на warning'ах. Тут надо либо от них избавляться, либо отключить под свою ответственность. Чтобы отключить, добавить в sources строку MSC_WARNING_LEVEL=/w.


Первый раз драйвер можно установить по-обычному, то есть через панель управления, а потом просто заменять файл в папке \windows\system32\drivers. Перед заменой надо сделать ему unload через диспетчер устройств: щелкнуть правой кнопкой по имени устройства и выбрать пункт «отключить». Потом скопировать файл .sys в названную папку и включить его обратно.


Очень помогает в работе программа DbgPrintDump, которая позволяет читать отладочную печать прямо в окошке, без второго компьютера.


У функции WdfIoCompleteRequest, которая вызывается в конце обработки любой файловой операции, есть поле info. Его обязательно надо делать ненулевым, потому что иначе код возврата status не передается в режим пользователя (всегда возвращается STATUS_SUCCESS).


К сожалению, значительная часть старых функций сделана deprecated, и, к сожалению, микрософт слов на ветер не бросает. HalGetInterruptVector работает, но не всегда. IoConnectInterrupt с ним срабатывает через раз. Сейчас, глядишь, работает, а после перезагрузки не работает.

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


Установка драйвера. Если нужен установочный пакет для драйвера, то есть файл setup.exe, чтобы пользователь не возился с .inf-файлами, то хорошо работает NSIS. Если один раз разобраться с синтаксисом настройки, то сделать инсталляцию потом в общем-то просто. Ссылка на NSIS. Также к нему надо скачать плугин InstDrv.
Другой путь -- через команду dpinst. Она устанавливает все драйверы в текущей папке, для которых есть inf и sys. Оба способа умеют и удалять. dpinst /u driver.inf, а NSIS немного сложнее.


Совсем отдельный случай -- установка не-PNP. Hardware ID для установки в PNP manager'е отсутствует, и тогда нужно вручную создать специальный hwid ROOT\MyDriver, на который потом и натравить inf-файл. Это можно сделать через программу devcon install driver.inf ROOT\MyDriver, но devcon.exe не разрешается включать в свои программы, поэтому надо взять ее исходники (они лежат в src\setup\devcon) и немного ее переделать, что как раз разрешается.
Как уже было сказано, вызывать функции с неправильным IRQL строго воспрещается. Практически это выливается в неприятные вещи, такие, как понижение этого самого IRQL. У меня был такой случай, вещь, теоретически абсолютно невозможная, обработчик прерывания сам прерывался во время работы. Все, конечно, сразу же зависало, потому что, как оказалось, в обработчике вызывалась функция KeSetSemaphore. А в документации ясно сказано, что вызывать ее из DIRQL нельзя ни в коем случае. Она-то и понижала IRQL и снимала маскрирование прерываний.