前面介绍的派遣函数处理过于简单,下面带领读者对派遣函数一步步进行扩充。首先介绍一个重要数据结构--IO_STACK LOCATION,即I/O堆栈,这个数据结构和IRP紧密相连。
在前面,曾经介绍过驱动程序的层次结构。驱动对象会创建一个个的设备对象,
并将这些设备对象“叠”成一个垂直结构。这种垂直的结构很像栈,因此被称为“设备栈”。
IRP会被操作系统发送到设备栈的顶层,如果顶层的设备对象的派遣函数结束了IRP的请求,
则这次 IO 请求结束。如果没有将IRP的请求结束,那么操作系统将IRP转发到设备栈的下一层设备处理。如果这个设备的派遣函数依然不能结束IRP请求,则会继续向下层设备转发。
因此,一个IRP可能会被转发多次。为了记录IRP在每层设备中做的操作,IRP会有一个 IO_STACK_LOCATION 数组。数组的元素数应该大于 IRP 穿越过的设备数。每个IO_STACK_LOCATION 元素记录着对应设备中做的操作。对于本层设备对应的IO_STACK_LOCATION,可以通过IoGetCurrentlrpStackLocation 函数得到,如:
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
下面的代码增加了派遣角数的难度,演示了派遣函数如何获得当前IO_STACK_LOCATION, 以及如何获得IRP的类型。在DriverEntry中将所有的IRP类型都和一个派遣函数相关联,下面是DriverEntry中的代码片段:
pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin;
然后在派遣函数中分辨出是IRP的类型,并打出相应的1og信息:
NTSTATUS HelloDDKDispatchRoutin(IN PDRIVER_OBJECT DriverObject, IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKDispatchRoutin\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
// 建立一个字符串数组与IRP类型对应起来
static char* irpname[] =
{
"IRP_MJ_CREATE",
"IRP_MJ_CREATE_NAMED_PIPE",
"IRP_MJ_CLOSE",
"IRP_MJ_READ",
"IRP_MJ_WRITE",
"IRP_MJ_QUERY_INFORMATION",
"IRP_MJ_SET_INFORMATION",
"IRP_MJ_QUERY_EA",
"IRP_MJ_SET_EA",
"IRP_MJ_FLUSH_BUFFERS",
"IRP_MJ_QUERY_VOLUME_INFORMATION",
"IRP_MJ_SET_VOLUME_INFORMATION",
"IRP_MJ_DIRECTORY_CONTROL",
"IRP_MJ_FILE_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CONTROL",
"IRP_MJ_INTERNAL_DEVICE_CONTROL",
"IRP_MJ_SHUTDOWN",
"IRP_MJ_LOCK_CONTROL",
"IRP_MJ_CLEANUP",
"IRP_MJ_CREATE_MAILSLOT",
"IRP_MJ_QUERY_SECURITY",
"IRP_MJ_SET_SECURITY",
"IRP_MJ_POWER",
"IRP_MJ_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CHANGE",
"IRP_MJ_QUERY_QUOTA",
"IRP_MJ_SET_QUOTA",
"IRP_MJ_PNP",
"IRP_MJ_PNP_POWER",
"IRP_MJ_MAXIMUM_FUNCTION",
"IRP_MJ_SCSI"
};
UCHAR type = stack->MajorFunction;
if (type >= arraysize(irpname))
KdPrint(("\t Unknown IRP, major type %x\n", type));
else
KdPrint(("\t %s\n", irpname[type]));
// 对一般IRP的简单操作,后面会介绍IRP更复杂的操作
NTSTATUS status = STATUS_SUCCESS;
// 设置IRP完成状态
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
// 结束IRP请求
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("Leave HelloDDKDispatchRoutin\n"));
return status;
}
将驱动程序成功加载后,执行应用程序。应用程序就是上节介绍的“打开”和“关闭”设备,
随后用Dbgview查看驱动程序输出的log信息。可以发现,依次进入派遣函数的是
IRP_MJ_CREATE、IRP_MJ_CLEANUP和IRP_MJ_CLOSE。图中列出了用Dbgview输出的log信息。