| [Назад] [Далее] | ![]() |
В Windows, так же как и в DOS, существует еще один вид исполнимых файлов — драйверы устройств. Windows 3.x и Windows 95 используют одну модель драйверов, Windows NT — другую, a Windows 98 — уже третью, хотя и во многом близкую к модели Windows NT. В Windows 3.x/Windows 95 используются два типа драйверов устройств — виртуальные драйверы (VxD), выполняющиеся с уровнем привилегий 0 (обычно имеют расширение .386 для Windows 3.x и .VXD для Windows 95), и непривилегированные драйверы, исполняющиеся, как и обычные программы, с уровнем привилегий 3 (обычно имеют расширение .DRV). Windows NT использует несовместимую модель драйверов, так называемую kernel-mode (режим ядра). На основе модели kernel-mode с добавлением поддержки технологии PNP и понятия потоков данных в 1996 году была основана модель WDM (win32 driver model), которая теперь используется в Windows 98 и NT и, по-видимому, будет играть главную роль в дальнейшем.
Как и следовало ожидать, основным средством создания драйверов является ассемблер, и хотя применение С здесь возможно (в отличие от случая драйверов DOS), оно оказывается гораздо сложнее, чем применение ассемблера, — функции, к которым обращается драйвер, могут передавать параметры в регистрах, сегменты, из которых состоит драйвер, должны называться определенным образом, и т.д. Практически при создании драйверов часто пользуются одновременно С и ассемблером или С и специальным пакетом, который включает в себя все необходимые для инициализации действия.
Чтобы самостоятельно создавать драйверы для любой версии Windows, необходим комплект программ, документации, включаемых файлов и библиотек, распространяемый Microsoft, который называется DDK (Drivers Development Kit). (DDK для Windows NT и Windows 98 распространяются бесплатно.)
Мы не будем рассматривать программирование драйверов для Windows в деталях, так как этой теме посвящено достаточно много литературы, не говоря уже о документации, прилагающейся к DDK. Чтобы создать драйвер, в любом случае лучше всего начать с одного из прилагающихся примеров и изменять/добавлять процедуры инициализации, обработчики сообщений, прерываний и исключений, обработчики для API, предоставляемого драйвером, и т.д. Рассмотрим только, как выглядит исходный текст драйвера, потому что он несколько отличается от привычных нам ассемблерных программ.
Любой драйвер начинается с директивы include vmm.inc, которая включает файл, содержащий определения используемых сегментов и макроопределений. Макроопределения вида VXD_LOCKED_CODE_SEG/VXD_LOCKED_CODE_ENDS соответствуют директивам начала и конца соответствующих сегментов (в данном случае сегмента _LTEXT). Другие два важных макроопределения Declare_Virtual_Device и VMMCall/WDMCall. Первое — это просто определение, получающее в качестве параметров идентификатор драйвера, название, версию, порядок загрузки и адреса основных процедур драйвера, из которых строится заголовок драйвера. Второе — это замена команды call, получающая в качестве параметра имя функции VMM или WDM, к которой надо обратиться. Например, элемент кода драйвера BIOSXLAT, перехватывающий прерывание 10h, выглядит следующим образом:
VxD_ICODE_SEG ; начало сегмента _ITEXT (сегмент кода
; инициализации, исполняющийся
; в защищенном режиме, который удаляется
; из памяти после сообщения Init_Complete)
BeginProc BIOSXlat_Sys_Critical_Init ; процедура, которая вызывается
; для обработчика сообщения
; Sys_Critical_Init - первого сообщения,
; которое получает драйвер.
; Обычно обработчики сообщений должны
; сохранять регистры ЕВХ, EDI, ESI и ЕВР,
; хотя в этом случае этого можно не делать
mov esi,OFFSET32 BIOSXlat_Int10 ; адрес обработчика INT 10h
; в регистр ESI. Важно использовать
; макроопределение OFFSET32 всюду
; вместо offset
mov edx,10h ; любое число, которое будет
; помещаться в EDX при вызове
; регистрируемого обработчика
VMMCall Allocate_PM_Call_Back ; зарегистрировать
; точку входа, обращаясь
; к которой, программы из защищенного
; режима в VM будут передавать
; управление процедуре в драйвере
;(но не win32-nporpaммa) -
; они должны использовать
; DeviceIOControl для работы
; с драйверами,
jc BXLSCI_NoPM ; если CF = 1 - произошла ошибка
xchg edx,eax ; точку входа - в EDX,
; число 10h - в ЕАХ
; (теперь это - номер перехватываемого прерывания для Set_PM_Int_Vector)
mov ecx,edx
shr ecx,10h ; селектор точки входа
movzx edx,dx ; смещение точки входа
VMMCall Set_PM_Int_Vector ; установить обработчик
; прерывания INT 10h.
; Если эта функция вызвана до Sys_VM_Init, установленный обработчик
; становится звеном цепочки обработчиков для всех виртуальных машин.
; После того как прерывание проходит по цепочке обработчиков
; в защищенном режиме, оно отображается в V86, точно так же, как в DPMI
; [код перехвата других прерываний]
EndProc BIOSXlat_Sys_Critical_Init ; конец процедуры
VxD_ICODE_ENDS ; конец сегмента инициализации
Соответственно, чтобы эта процедура была вызвана для сообщения Sys_Critical_Init, в сегменте фиксированного кода _LTEXT должна быть следующая запись:
VxD_LQCKED_CODE_SEG ; начало сегмента _LTEXT
BeginProc BIOSXlat_Control ; начало процедуры
Control_Dispatch Sys_Critical_Init,\BIOSXlat_Sys_Critical_Init
; при помощи еще одного макроопределения из vmm.inc
; зарегистрировать процедуру BIOSXlat_Sys_Critical_Init
; как обработчик сообщения Sys_Critical_Init
clc ; процедура-обработчик управляющих
; сообщений должна возвращать CF = 0
ret
EndProc BIOSXlat_Control ; конец процедуры
VxD_LOCKED_CODE_ENDS ; конец сегмента _LTEXT
И наконец, процедура BIOSXlat_Control регистрируется как процедура, получающая управляющие сообщения, в заголовке драйвера:
; первая строка после .386р и include vmm.inc:
Declare_Virtual_Device BlOSXlat, 1, 0, BIOSXlat_Control,\ BIOSXiat_Device_ID,
BIOSXlat_Init_Order
Это не слишком сложно и, пользуясь примерами и документацией из DDK и отладчиком SoftICE, можно справиться практически с любой задачей, для которой имеет смысл создавать драйвер.