在调用DeviceIoControl时,应用层的输入缓冲区的内容被复制到IRP中的Irp->AssociatedIrp.SystemBuffer内存地址中。这个步骤和缓冲区模式IOCTL处理一样。
但是,对于DeviceIoControl指定的输出缓冲区的处理,操作系统将该缓冲区锁定,然后在内核模式地址空间中重新映射一段地址。
在驱动程序中,METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT模式都以相同方式处理,仅有的不同是它们访问用户模式缓冲区所需要的访问权限:
METHOD_IN_DIRECT: 需要读权限
METHOD_OUT_DIRECT:需要读和写权限。
使用这两种模式,IO管理器会为输入数据提供一个内核模式拷贝缓冲区,该缓冲区地址存放在Irp->AssociatedIrp.SystemBuffer中,将应用层传递的数据拷贝到该内核缓冲区中。但是为输出数据缓冲区创建一个MDL。我们可以使用MmGetSystemAddressForMdlSafe来获得该应用层虚拟缓冲区所对应的内核地址。从而对其进行访问。
例子:
NTSTATUS DeviceIoControl(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IrpSp;
ULONG InputLength;
PVOID InputBuffer;
ULONG OutputLength;
PVOID OutputBuffer;
// 获得当前堆栈位置
IrpSp = IoGetCurrentIrpStackLocation(Irp);
// 得到输入缓冲区的大小
InputLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
// 得到输入缓冲区的地址
InputBuffer = Irp->AssociatedIrp.SystemBuffer;
// 得到输出缓冲区的大小
OutputLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
// 得到输出缓冲区在内核层的映射地址
OutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}