(注:本篇的原理部分均摘自罗云彬大侠翻译的驱动开发教程)
在前面的两篇教程中我们写了三个玩具驱动程序,为什么说是玩具驱动呢?因为它们确确实实是驱动程序,而且也能完成一些有趣的功能,但是它们都不完整,没有同用户交流的功能,这一篇就让我们来完成一个简单的全功能驱动程序。
在写程序之前,我们有必要了解一些基础知识。
在用户模式下,我们可以通过访问某个地址来直接调用dll中的函数,但是在内核模式下,从系统的稳定性考虑,这样做是非常危险的。所以,系统提供了和内核模式通讯的媒介--I/O管理器,它是I/O子系统的部件之一。I/O管理器将应用程序、系统部件和设备连接起来,并定义了一个架构来支持设备驱动程序。下图是I/O管理器如何在用户模式程序和驱动程序之间进行沟通的简单图解。
screen.width-333)this.width=screen.width-333" dypop="按此在新窗口浏览图片">
一般来说,用户模式的操作都被转换成了对具体硬件设备的I/O操作,仅对于某些设备,设备由驱动程序来创建和控制,这些设备就是虚拟设备。当然,创建这些设备并不意味着你创造了什么硬件,而仅仅是在内存中创建了一个新的对象而已。每个对象和一个物理设备或者逻辑设备对应,用于描述它们的特征。
创建设备后,驱动程序告诉I/O管理器:“这里有个我控制的设备,如果你收到了操作这个设备的I/O请求的话,直接发给我好了,剩下的由我来搞定!”。驱动程序知道如何对自己管理的设备进行I/O操作,I/O管理器唯一的职责在于创建I/O请求并把它发送给适当的设备驱动程序。用户模式的代码不知道(也不必知道)其中的细节,也不用知道究竟是哪个驱动程序在管理哪个设备。
下面先让我们来看一下用户模式下的控制程序:
在前面的两篇教程中我们写了三个玩具驱动程序,为什么说是玩具驱动呢?因为它们确确实实是驱动程序,而且也能完成一些有趣的功能,但是它们都不完整,没有同用户交流的功能,这一篇就让我们来完成一个简单的全功能驱动程序。
在写程序之前,我们有必要了解一些基础知识。
在用户模式下,我们可以通过访问某个地址来直接调用dll中的函数,但是在内核模式下,从系统的稳定性考虑,这样做是非常危险的。所以,系统提供了和内核模式通讯的媒介--I/O管理器,它是I/O子系统的部件之一。I/O管理器将应用程序、系统部件和设备连接起来,并定义了一个架构来支持设备驱动程序。下图是I/O管理器如何在用户模式程序和驱动程序之间进行沟通的简单图解。
screen.width-333)this.width=screen.width-333" dypop="按此在新窗口浏览图片">
一般来说,用户模式的操作都被转换成了对具体硬件设备的I/O操作,仅对于某些设备,设备由驱动程序来创建和控制,这些设备就是虚拟设备。当然,创建这些设备并不意味着你创造了什么硬件,而仅仅是在内存中创建了一个新的对象而已。每个对象和一个物理设备或者逻辑设备对应,用于描述它们的特征。
创建设备后,驱动程序告诉I/O管理器:“这里有个我控制的设备,如果你收到了操作这个设备的I/O请求的话,直接发给我好了,剩下的由我来搞定!”。驱动程序知道如何对自己管理的设备进行I/O操作,I/O管理器唯一的职责在于创建I/O请求并把它发送给适当的设备驱动程序。用户模式的代码不知道(也不必知道)其中的细节,也不用知道究竟是哪个驱动程序在管理哪个设备。
下面先让我们来看一下用户模式下的控制程序:
| program VirToPhys; {$APPTYPE CONSOLE} uses SysUtils, Windows, WinSvc, Dialogs, nt_status; const NUM_DATA_ENTRY =4; DATA_SIZE = sizeof(DWORD) * NUM_DATA_ENTRY; _DELETE = $10000; var hSCManager:THANDLE; hService:THANDLE; acModulePath: array [0..MAX_PATH] of char; _ss:SERVICE_STATUS; hDevice:THANDLE; adwInBuffer: array [0..NUM_DATA_ENTRY] of DWORD; adwOutBuffer: array [0..NUM_DATA_ENTRY] of DWORD; dwBytesReturned:DWORD; IOCTL_GET_PHYS_ADDRESS: DWORD; lpTemp: PChar; iRetValue: boolean; {生成控制码} function CTL_CODE(DeviceType, Func, Method, Access: DWORD): DWORD; begin result := (((DeviceType) SHL 16) OR ((Access) SHL 14) OR ((Func) SHL 2) OR (Method)); end; begin IOCTL_GET_PHYS_ADDRESS := CTL_CODE(FILE_DEVICE_UNKNOWN, $800, METHOD_BUFFERED, FILE_READ_ACCESS + FILE_WRITE_ACCESS); hSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS); if hSCManager <> 0 then begin GetFullPathName(PChar('VirtToPhys.sys'), sizeof(acModulePath), acModulePath, lpTemp); hService := CreateService(hSCManager, 'VirtToPhys', 'Virtual To Physical Address Converter', SERVICE_START + SERVICE_STOP + _DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, acModulePath, nil, nil, nil, nil, nil); if hService <> 0 then begin {驱动程序的DriverEntry过程将被调用} if StartService(hService, 0, lpTemp) = true then begin {驱动程序将接收IRP_MJ_CREATE I/O请求包(IRP)} hDevice := CreateFile('\\.\slVirtToPhys', GENERIC_READ+GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); if hDevice <> INVALID_HANDLE_VALUE then begin {准备送往驱动程序的数据包} adwInBuffer[0] := GetModuleHandle(nil); adwInBuffer[1] := GetModuleHandle('kernel32.dll'); adwInBuffer[2] := GetModuleHandle('user32.dll'); adwInBuffer[3] := GetModuleHandle('comctl32.dll'); {驱动程序将接收IRP_MJ_DEVICE_CONTROL I/O请求包} iRetValue := DeviceIoControl(hDevice, IOCTL_GET_PHYS_ADDRESS, @adwInBuffer, sizeof(adwInBuffer), @adwOutBuffer, sizeof(adwOutBuffer), dwBytesReturned, nil); if (iRetValue = true) and (dwBytesReturned <> 0) then begin {取程序名} GetModuleFileName(adwInBuffer[0], acModulePath, sizeof(acModule |

本文档介绍了如何使用Delphi编写一个完整的驱动程序,讲解了驱动程序与用户模式程序的交互机制,包括I/O管理器的作用、设备对象的创建以及如何通过CreateFile函数和DeviceIoControl函数进行通讯。文中详细阐述了驱动程序的创建过程、设备对象、符号连接以及IRP处理,展示了如何实现虚拟设备的创建与控制。

1659

被折叠的 条评论
为什么被折叠?



