一,基本数据结构:
二,DriverEntry()函数
函数DriverEntry是每个驱动程序中必须的。如同Win32应用程序里的WinMain。DriverEntry的第一个参数就是一个DRIVER_OBJECT的指针。这个DRIVER_OBJECT结构就对应当前编写的驱动程序。其内存是Windows系统已经分配的。 第二个参数RegistryPath是一个字符串。代表一个注册表子键。这个子键是专门分配给这个驱动程序使用的。用于保存驱动配置信息到注册表中。 DriverEntry的返回值决定这个驱动的加载是否成功。如果返回为STATUS_SUCCESS,则驱动将成功加载。否则,驱动加载失败。
DRIVER_OBJECT中含有分发函数指针。这些函数用来处理发到这个驱动的各种请求。Windows总是自己调用DRIVER_OBJECT下的分发函数来处理这些请求。所以编写一个驱动程序,本质就是自己编写这些处理请求的分发函数。
DriverObject->MajorFunctions[i] = MyDispatchFunction;
驱动程序和系统其他组件之间的交互是通过给设备发送或者接受发给设备的请求来交互的,
1 /************************************************************************
2
3 * 函数名称:DriverEntry
4
5 * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
6
7 * 参数列表:
8
9 pDriverObject:从I/O管理器中传进来的驱动对象
10
11 pRegistryPath:驱动程序在注册表的中的路径
12
13 * 返回值:返回初始化驱动状态
14
15 *************************************************************************/
16
17 #pragma INITCODE
18
19 extern "C" NTSTATUS DriverEntry (
20
21 IN PDRIVER_OBJECT pDriverObject,
22
23 IN PUNICODE_STRING pRegistryPath )
24
25 {
26
27 NTSTATUS status;
28
29 KdPrint(("Enter DriverEntry\n"));
30
31 //注册其他驱动调用函数入口
32
33 pDriverObject->DriverUnload = HelloDDKUnload;
34
35 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
36
37 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
38
39 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
40
41 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
42
43 //创建驱动设备对象
44
45 status = CreateDevice(pDriverObject);
46
47 KdPrint(("DriverEntry end\n"));
48
49 return status;
50
51 }
我们可以看到DriverEntry 首先是定义了一些变量,然后调用IoCreateDevice 创建设备对象,紧接着调用IoCreateSymbolicLink 创建符号链接。HelloDDKDispatchRoutine等是驱动程序在向Windows 的I/O 管理器注册一些回调函数。上面代码的含义是:当驱动程序将被卸载时自动调用HelloDDKUnload例程;当驱动程序接收到 IRP_MJ_CREATE 时自动调用HelloDDKDispatchRoutine。现在可以把IRP_MJ_CREATE理解成类似ring3的“消息”,当我们的驱动程序接收到不同的IRP就表明发生了不同的事件,然后我们及时给予处理。
三,应用程序如何和驱动创建的设备交互
如果驱动程序要和应用程序之间通信,则应该生成设备。此外还必须为设备生成应用程序可以访问的符号链接。
#include <ntifs.h>
NTSTATUS DriverEntry(
PDRIVER_OBJECT driver,
PUNICODE_STRING reg_path) {
NTSTATUS status;
PDEVICE_OBJECT device; //设备名
UNICODE_STRING device_name = RTL_CONSTANT_STRING("\\Device\\MyCDO"); // 符号链接名
UNICODE_STRING symb_link = RTL_CONSTANT_STRING("\\DosDevices\\MyCDOSL"); //生成设备对象
status = IoCreateDevice( driver,
0,
device_name,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&device);
// 如果不成功,就返回。
if(!NT_SUCCESS(status))
return status;
// 生成符号链接
status = IoCreateSymbolicLink(
&symb_link,
&device_name);
if(!NT_SUCCESS(status)) {
IoDeleteDevice(device);
return status;
}
// 设备生成之后,打开初始化完成标记
device->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
}
这个驱动成功加载之后,生成一个名叫“\Device\MyCDO”的设备。然后在给这个设备生成了一个符号链接名字叫做“\DosDevices\MyCDOSL”。应用层可以通过打开这个符号链接来打开设备。应用层可以调用CreateFile就像打开文件一样打开。只是路径应该是“”\\.\ MyCDOSL”。前面的“\\.\”意味后面是一个符号链接名,而不是一个普通的文件。
真正创建设备的是IoCreateDevice函数,此函数在DDk中声明如下
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject, //设备对象
IN ULONG DeviceExtensionSize, //扩展大小
IN PUNICODE_STRING DeviceName OPTIONAL, //设备对象名
IN DEVICE_TYPE DeviceType, //设备类型
IN ULONG DeviceCharacteristics, //设备对象特征
IN BOOLEAN Exclusive, //设置设备对象是否可在内核模式下使用
OUT PDEVICE_OBJECT *DeviceObject //输出参数,I/o管理器负责创建这个设备对象
);
1: 设备名称用unicode 字符指定,并且字符串必须是 “\Device\设备名 的形式,如磁盘分区C盘,为\Device\harddisk\volume1,当然你也可以不用指定设备名,但是这样做,不是设备就不需要设备名了,而是I/O管理器为设备创建一设备名,如果指定了设备名,只能被内核模式下的其他驱动程序识别,但是在用户模式下的应用程序能识别它吗?答案是否定的; 为了让用户模式的应用程序能够识别或者是使用设备,有两种方法可以使用: 一是通过创建一符号链接给用户模式应用程序使用,二是使用设备接口,一般NT中很少使用设备接口,而WDM中使用的较多;
2:你可以将符号链接理解为是设备对象的一个别名,设备对象的名称只能被内核模式下的驱动识别,而别名也可以被用户模式下的应用程序识别.创建符号链接用如下函数:
四,驱动的派遣程序
五,WDM驱动:
WDM驱动和NT驱动的主要区别是WDM驱动支持即插即用。