zoukankan      html  css  js  c++  java
  • 31、分层驱动程序

    把功能复杂的驱动分解成多个简单的驱动。多个分层驱动程序形成一个设备堆栈,IRP请求首先发送到设备堆栈的顶层,然后以次穿越每层的设备堆栈,最终完成IRP的请求。

    1、相关概念

    分层驱动是指两个或两个以上的驱动程序,他们分别创建设备对象,并且形成一个由高到低的设备对象栈。IRP请求一般送到设备栈最顶层的设备对象。顶层设备对象可以处理该IRP请求,也可以向下转发。IRP请求结束时,按原路返回。

    1_DEVICE_OBJECT结构体,需要注意3个参数:

    DriverObject 设备对象对应的驱动对象

    NextDevice 记录下一个设备对象的指针

    AttachedDevice 记录当前设备对象挂载的设备对象。

    WDM驱动程序就属于分层驱动程序。最简单的WDM驱动程序分为两层:一层是PDO(物理设备对象),一层是FDO(功能设备对象),FDO挂载在PDO之上。PDO实现即插即用的功能,FDO完成逻辑功能,而将一些硬件相关请求发往PDL

    挂载指的是将高一层的设备对象挂载在低一层的设备对象上,从而形成一个设备栈。

    IoAttachDeviceToDeviceStack 挂载

    IoDetachDevice 卸载

    2I/O 堆栈

    用一个叫做IO_STACK_LOCATION的数据结构来保存;它和设备堆栈紧密结合。在IRP中,有一个指向IO_STACK_LOCATION数组的指针。调用IoAllocateIrp创建Irp时,有一个StackSize,就是IO_STACK_LOCATION数组的大小。

    clip_image002

    图示 P322

    3)向下转发IRP

    3种方法处理IRP:直接处理;调用StartIo,向下转发。

    一个设备堆栈对应一个IO_STACK_LOCATION堆栈元素。IRP内部有一个指针指向当前正使用的IO_STACK_LOCATIONIoGetCurrentIrpStackLocation 获得。

    每次调用IoCallDriver时,内核函数会将IRP的当前指针向下移一个单位。而IoSkipCurrentIrpStackLocation 用来将当前I/O堆栈往回(上)移一个单位。

    When sending an IRP to the next-lower driver, your driver can call IoSkipCurrentIrpStackLocation if you do not intend to provide an IoCompletion routine (the address of which is stored in the driver's IO_STACK_LOCATION structure). If you call IoSkipCurrentIrpStackLocation before calling IoCallDriver, the next-lower driver receives the same IO_STACK_LOCATION that your driver received.

    If you intend to provide an IoCompletion routine for the IRP, your driver should call IoCopyCurrentIrpStackLocationToNext instead of IoSkipCurrentIrpStackLocation.

    代码
    1 /************************************************************************
    2 * 函数名称:DriverEntry
    3 * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
    4 * 参数列表:
    5 pDriverObject:从I/O管理器中传进来的驱动对象
    6 pRegistryPath:驱动程序在注册表的中的路径
    7 * 返回 值:返回初始化驱动状态
    8 *************************************************************************/
    9  #pragma INITCODE
    10  extern "C" NTSTATUS DriverEntry (
    11 IN PDRIVER_OBJECT pDriverObject,
    12 IN PUNICODE_STRING pRegistryPath )
    13 {
    14 NTSTATUS ntStatus;
    15 KdPrint(("DriverB:Enter B DriverEntry\n"));
    16
    17 //注册其他驱动调用函数入口
    18   pDriverObject->DriverUnload = HelloDDKUnload;
    19 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKCreate;
    20 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKClose;
    21 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
    22 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
    23
    24 UNICODE_STRING DeviceName;
    25 RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );
    26
    27 PDEVICE_OBJECT DeviceObject = NULL;
    28 PFILE_OBJECT FileObject = NULL;
    29 //寻找DriverA创建的设备对象
    30   ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);
    31
    32 if (!NT_SUCCESS(ntStatus))
    33 {
    34 KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus ));
    35 return ntStatus;
    36 }
    37
    38 //创建自己的驱动设备对象
    39   ntStatus = CreateDevice(pDriverObject);
    40
    41 if ( !NT_SUCCESS( ntStatus ) )
    42 {
    43 ObDereferenceObject( FileObject );
    44 DbgPrint( "IoCreateDevice() 0x%x!\n", ntStatus );
    45 return ntStatus;
    46 }
    47
    48 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) pDriverObject->DeviceObject->DeviceExtension;
    49
    50 PDEVICE_OBJECT FilterDeviceObject = pdx->pDevice;
    51
    52 //将自己的设备对象挂载在DriverA的设备对象上
    53 PDEVICE_OBJECT TargetDevice = IoAttachDeviceToDeviceStack( FilterDeviceObject,
    54 DeviceObject );
    55 //将底层设备对象记录下来
    56 pdx->TargetDevice = TargetDevice;
    57
    58 if ( !TargetDevice )
    59 {
    60 ObDereferenceObject( FileObject );
    61 IoDeleteDevice( FilterDeviceObject );
    62 DbgPrint( "IoAttachDeviceToDeviceStack() 0x%x!\n", ntStatus );
    63 return STATUS_INSUFFICIENT_RESOURCES;
    64 }
    65
    66 FilterDeviceObject->DeviceType = TargetDevice->DeviceType;
    67 FilterDeviceObject->Characteristics = TargetDevice->Characteristics;
    68 FilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    69 FilterDeviceObject->Flags |= ( TargetDevice->Flags & ( DO_DIRECT_IO |
    70 DO_BUFFERED_IO ) );
    71 ObDereferenceObject( FileObject );
    72
    73 KdPrint(("DriverB:B attached A successfully!\n"));
    74
    75 KdPrint(("DriverB:Leave B DriverEntry\n"));
    76 return ntStatus;
    77 }
    78 /************************************************************************
    79 * 函数名称:HelloDDKUnload
    80 * 功能描述:负责驱动程序的卸载操作
    81 * 参数列表:
    82 pDriverObject:驱动对象
    83 * 返回 值:返回状态
    84 *************************************************************************/
    85 #pragma PAGEDCODE
    86 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
    87 {
    88 PDEVICE_OBJECT pNextObj;
    89 KdPrint(("DriverB:Enter B DriverUnload\n"));
    90 pNextObj = pDriverObject->DeviceObject;
    91
    92 while (pNextObj != NULL)
    93 {
    94 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
    95 pNextObj->DeviceExtension;
    96 pNextObj = pNextObj->NextDevice;
    97 //从设备栈中弹出
    98 IoDetachDevice( pDevExt->TargetDevice);
    99 //删除该设备对象
    100 IoDeleteDevice( pDevExt->pDevice );
    101 }
    102 KdPrint(("DriverB:Enter B DriverUnload\n"));
    103 }
    104
    105 /************************************************************************
    106 * 函数名称:HelloDDKDispatchRoutine
    107 * 功能描述:对读IRP进行处理
    108 * 参数列表:
    109 pDevObj:功能设备对象
    110 pIrp:从IO请求包
    111 * 返回 值:返回状态
    112 *************************************************************************/
    113 #pragma PAGEDCODE
    114 NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
    115 IN PIRP pIrp)
    116 {
    117 KdPrint(("DriverB:Enter B HelloDDKDispatchRoutine\n"));
    118 NTSTATUS ntStatus = STATUS_SUCCESS;
    119 // 完成IRP
    120 pIrp->IoStatus.Status = ntStatus;
    121 pIrp->IoStatus.Information = 0; // bytes xfered
    122 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    123 KdPrint(("DriverB:Leave B HelloDDKDispatchRoutine\n"));
    124 return ntStatus;
    125 }
    126
    127 #pragma PAGEDCODE
    128 NTSTATUS HelloDDKCreate(IN PDEVICE_OBJECT pDevObj,
    129 IN PIRP pIrp)
    130 {
    131 KdPrint(("DriverB:Enter B HelloDDKCreate\n"));
    132 NTSTATUS ntStatus = STATUS_SUCCESS;
    133 //
    134 // // 完成IRP
    135 // pIrp->IoStatus.Status = ntStatus;
    136 // pIrp->IoStatus.Information = 0; // bytes xfered
    137 // IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    138
    139 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    140
    141 IoSkipCurrentIrpStackLocation (pIrp);
    142
    143 ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
    144
    145 KdPrint(("DriverB:Leave B HelloDDKCreate\n"));
    146
    147 return ntStatus;
    148 }
    149
    150 #pragma PAGEDCODE
    151 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    152 IN PIRP pIrp)
    153 {
    154 KdPrint(("DriverB:Enter B HelloDDKCreate\n"));
    155 NTSTATUS ntStatus = STATUS_SUCCESS;
    156 //将自己完成IRP,改成由底层驱动负责
    157
    158 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    159
    160 //调用底层驱动
    161 IoSkipCurrentIrpStackLocation (pIrp);
    162
    163 ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
    164
    165 KdPrint(("DriverB:Leave B HelloDDKCreate\n"));
    166
    167 return ntStatus;
    168 }
    169
    170 #pragma PAGEDCODE
    171 NTSTATUS HelloDDKClose(IN PDEVICE_OBJECT pDevObj,
    172 IN PIRP pIrp)
    173 {
    174 KdPrint(("DriverB:Enter B HelloDDKClose\n"));
    175 NTSTATUS ntStatus = STATUS_SUCCESS;
    176
    177 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    178
    179 IoSkipCurrentIrpStackLocation (pIrp);
    180
    181 ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
    182
    183 KdPrint(("DriverB:Leave B HelloDDKClose\n"));
    184
    185 return ntStatus;
    186 }
    挂载设备对象代码示例 P324

    转发IRP代码示例 P326 如上代码示例Read

    2、完成例程

    在将IRP发送给低层驱动或者其他驱动之前,可以对IRP设置一个完成例程。一旦底层驱动将IRP完成后,IRP完成例程将被触发,可以通过这个原理来获得通知。

    IoSetCompletionRoutine ,如果参数CompletionRoutine NULL,则意味着完成例程取消。IRPIoCompleteRequest 完成时,会一层层出栈,如果遇到完成例程,则调用完成例程。

    当调用IoCallDriver后,当前驱动就失去了对IRP的控制;如果此时设置IRP的属性,会引起系统崩溃。完成例程返回两种状态,STATUS_SUCCESSSTATUS_MORE_PROCESSING_REQUIRED。如果返回是 STATUS_MORE_PROCESSING_REQUIRED,则本层设备堆栈重新获得IRP的控制权,并且设备栈不会向上弹出,也就是向上“回卷” 设备栈停止,此时可以再次向底层发送IRP

    1)传播Pending

    IRP的堆栈向上回卷时,底层I/O堆栈的Control域的SL_PENDING_RETURNED位必须传播到上一层。如果本层没有设备完成例程,传播是自动的;如果设备了完成例程,则需要程序员自己实现。

    注意:只能在完成例程中设置。

    2)返回STATUS_SUCCESS

    如果是STATUS_SUCCESS,则继续回卷。

    代码
    1 NTSTATUS
    2 MyIoCompletion(
    3 IN PDEVICE_OBJECT DeviceObject,
    4 IN PIRP Irp,
    5 IN PVOID Context
    6 )
    7 {
    8 //进入此函数标志底层驱动设备将IRP完成
    9 KdPrint(("Enter MyIoCompletion\n"));
    10 if (Irp->PendingReturned)
    11 {
    12 //传播pending位
    13 IoMarkIrpPending( Irp );
    14 }
    15 return STATUS_SUCCESS;//同STATUS_CONTINUE_COMPLETION
    16 }
    17
    18 #pragma PAGEDCODE
    19 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    20 IN PIRP pIrp)
    21 {
    22 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
    23 NTSTATUS ntStatus = STATUS_SUCCESS;
    24 //将自己完成IRP,改成由底层驱动负责
    25
    26 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    27
    28 //将当前IRP堆栈拷贝底层堆栈
    29 IoCopyCurrentIrpStackLocationToNext(pIrp);
    30
    31 //设置完成例程
    32 IoSetCompletionRoutine(pIrp,MyIoCompletion,NULL,TRUE,TRUE,TRUE);
    33
    34 //调用底层驱动
    35 ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
    36
    37 //当IoCallDriver后,并且完成例程返回的是STATUS_SUCCESS
    38 //IRP就不在属于派遣函数了,就不能对IRP进行操作了
    39 if (ntStatus == STATUS_PENDING)
    40 {
    41 KdPrint(("STATUS_PENDING\n"));
    42 }
    43 ntStatus = STATUS_PENDING;
    44
    45 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
    46
    47 return ntStatus;
    48 }
    示例代码 P333

    3STATUS_MORE_PROCESSING_REQUIRED

    如果是STATUS_MORE_PROCESSING_REQUIRED,则本层堆栈重新获得控制,并且该IRP从完成状态变成了未完成状态,需要再次完成,即执行IoCompleteRequest

    重新获得的IRP可以再次传下底层,也可以标志完成。

    代码
    1 NTSTATUS
    2 MyIoCompletion(
    3 IN PDEVICE_OBJECT DeviceObject,
    4 IN PIRP Irp,
    5 IN PVOID Context
    6 )
    7 {
    8
    9 if (Irp->PendingReturned == TRUE)
    10 {
    11 //设置事件
    12 KeSetEvent((PKEVENT)Context,IO_NO_INCREMENT,FALSE);
    13 }
    14
    15 return STATUS_MORE_PROCESSING_REQUIRED;
    16 }
    17
    18 #pragma PAGEDCODE
    19 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    20 IN PIRP pIrp)
    21 {
    22 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
    23 NTSTATUS ntStatus = STATUS_SUCCESS;
    24 //将自己完成IRP,改成由底层驱动负责
    25
    26 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    27
    28 //将本层的IRP堆栈拷贝到底层堆栈
    29 IoCopyCurrentIrpStackLocationToNext(pIrp);
    30
    31 KEVENT event;
    32 //初始化事件
    33 KeInitializeEvent(&event, NotificationEvent, FALSE);
    34
    35 //设置完成例程
    36 IoSetCompletionRoutine(pIrp,MyIoCompletion,&event,TRUE,TRUE,TRUE);
    37
    38 //调用底层驱动
    39 ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
    40
    41 if (ntStatus == STATUS_PENDING)
    42 {
    43 KdPrint(("IoCallDriver return STATUS_PENDING,Waiting ...\n"));
    44 KeWaitForSingleObject(&event,Executive,KernelMode ,FALSE,NULL);
    45 ntStatus = pIrp->IoStatus.Status;
    46 }
    47
    48 //虽然在底层驱动已经将IRP完成了,但是由于完成例程返回的是
    49 //STATUS_MORE_PROCESSING_REQUIRED,因此需要再次调用IoCompleteRequest!
    50 IoCompleteRequest (pIrp, IO_NO_INCREMENT);
    51
    52 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
    53
    54 return ntStatus;
    55 }
    示例代码 P335

    3)将IRP分成多个IRP

    通过一个例子来说明。

    clip_image003

    图示分层驱动模型 P336

    代码
    1 #pragma PAGEDCODE
    2 NTSTATUS
    3 HelloDDKReadCompletion(
    4 IN PDEVICE_OBJECT DeviceObject,
    5 IN PIRP Irp,
    6 IN PVOID Context
    7 )
    8 {
    9 KdPrint(("DriverB:Enter B HelloDDKReadCompletion\n"));
    10
    11 PMYDRIVER_RW_CONTEXT rwContext = (PMYDRIVER_RW_CONTEXT) Context;
    12 NTSTATUS ntStatus = Irp->IoStatus.Status;
    13
    14 ULONG stageLength;
    15
    16 if(rwContext && NT_SUCCESS(ntStatus))
    17 {
    18 //已经传送了多少字节
    19 rwContext->Numxfer += Irp->IoStatus.Information;
    20
    21 if(rwContext->Length)
    22 {
    23 //设定下一阶段读取字节数
    24 if(rwContext->Length > MAX_PACKAGE_SIZE)
    25 {
    26 stageLength = MAX_PACKAGE_SIZE;
    27 }
    28 else
    29 {
    30 stageLength = rwContext->Length;
    31 }
    32 //重新利用MDL
    33 MmPrepareMdlForReuse(rwContext->NewMdl);
    34
    35 IoBuildPartialMdl(Irp->MdlAddress,
    36 rwContext->NewMdl,
    37 (PVOID) rwContext->VirtualAddress,
    38 stageLength);
    39
    40 rwContext->VirtualAddress += stageLength;
    41 rwContext->Length -= stageLength;
    42
    43 IoCopyCurrentIrpStackLocationToNext(Irp);
    44 PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
    45
    46 nextStack->Parameters.Read.Length = stageLength;
    47
    48 IoSetCompletionRoutine(Irp,
    49 HelloDDKReadCompletion,
    50 rwContext,
    51 TRUE,
    52 TRUE,
    53 TRUE);
    54
    55 IoCallDriver(rwContext->DeviceExtension->TargetDevice,
    56 Irp);
    57
    58 return STATUS_MORE_PROCESSING_REQUIRED;
    59 }
    60 else
    61 {
    62 //最后一次传输
    63 Irp->IoStatus.Information = rwContext->Numxfer;
    64 }
    65 }
    66
    67 KdPrint(("DriverB:Leave B HelloDDKReadCompletion\n"));
    68 return STATUS_MORE_PROCESSING_REQUIRED;
    69 }
    70
    底层驱动示例代码 P337 见DriverA示例部分

    代码
    1 #pragma PAGEDCODE
    2 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    3 IN PIRP pIrp)
    4 {
    5 KdPrint(("DriverB:Enter B HelloDDKRead\n"));
    6 NTSTATUS status = STATUS_SUCCESS;
    7
    8 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
    9 pDevObj->DeviceExtension;
    10
    11 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    12
    13 ULONG totalLength;
    14 ULONG stageLength;
    15 PMDL mdl;
    16 PVOID virtualAddress;
    17 PMYDRIVER_RW_CONTEXT rwContext = NULL;
    18 PIO_STACK_LOCATION nextStack;
    19
    20 if (!pIrp->MdlAddress)
    21 {
    22 status = STATUS_UNSUCCESSFUL;
    23 totalLength = 0;
    24 goto HelloDDKRead_EXIT;
    25 }
    26
    27 //获取MDL的虚拟地址
    28 virtualAddress = MmGetMdlVirtualAddress(pIrp->MdlAddress);
    29 //获取MDL的长度
    30 totalLength = MmGetMdlByteCount(pIrp->MdlAddress);
    31
    32 KdPrint(("DriverB:(pIrp->MdlAddress)MmGetMdlVirtualAddress:%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
    33 KdPrint(("DriverB:(pIrp->MdlAddress)MmGetMdlByteCount:%d\n",MmGetMdlByteCount(pIrp->MdlAddress)));
    34
    35 //将总的传输,分成几个阶段,这里设定每次阶段的长度
    36 if(totalLength > MAX_PACKAGE_SIZE)
    37 {
    38 stageLength = MAX_PACKAGE_SIZE;
    39 }else
    40 {
    41 stageLength = totalLength;
    42 }
    43
    44 //创建新的MDL
    45 mdl = IoAllocateMdl((PVOID) virtualAddress,
    46 totalLength,
    47 FALSE,
    48 FALSE,
    49 NULL);
    50
    51 KdPrint(("DriverB:(new mdl)MmGetMdlVirtualAddress:%08X\n",MmGetMdlVirtualAddress(mdl)));
    52 KdPrint(("DriverB:(new mdl)MmGetMdlByteCount:%d\n",MmGetMdlByteCount(mdl)));
    53
    54 if(mdl == NULL)
    55 {
    56 KdPrint(("DriverB:Failed to alloc mem for mdl\n"));
    57 status = STATUS_INSUFFICIENT_RESOURCES;
    58 goto HelloDDKRead_EXIT;
    59 }
    60
    61 //将IRP的MDL做重新映射
    62 IoBuildPartialMdl(pIrp->MdlAddress,
    63 mdl,
    64 (PVOID) virtualAddress,
    65 stageLength);
    66 KdPrint(("DriverB:(new mdl)MmGetMdlVirtualAddress:%08X\n",MmGetMdlVirtualAddress(mdl)));
    67 KdPrint(("DriverB:(new mdl)MmGetMdlByteCount:%d\n",MmGetMdlByteCount(mdl)));
    68
    69 rwContext = (PMYDRIVER_RW_CONTEXT)
    70 ExAllocatePool(NonPagedPool,sizeof(MYDRIVER_RW_CONTEXT));
    71
    72 rwContext->NewMdl = mdl;
    73 rwContext->PreviousMdl = pIrp->MdlAddress;
    74 rwContext->Length = totalLength - stageLength;//还剩下多少没读取
    75 rwContext->Numxfer = 0; //读了多少字节
    76 rwContext->VirtualAddress = ((ULONG_PTR)virtualAddress + stageLength);//下一阶段开始读取的地址
    77 rwContext->DeviceExtension = pDevExt;
    78
    79 //拷贝到底层堆栈
    80 IoCopyCurrentIrpStackLocationToNext(pIrp);
    81
    82 nextStack = IoGetNextIrpStackLocation(pIrp);
    83 //根据底层驱动的实现,底层驱动有可能读取这个数值,也有可能读取mdl的length。
    84 nextStack->Parameters.Read.Length = stageLength;
    85
    86 pIrp->MdlAddress = mdl;
    87
    88 //设定完成例程
    89 IoSetCompletionRoutine(pIrp,
    90 (PIO_COMPLETION_ROUTINE)HelloDDKReadCompletion,
    91 rwContext,
    92 TRUE,
    93 TRUE,
    94 TRUE);
    95
    96 IoCallDriver(pDevExt->TargetDevice,pIrp);
    97
    98 pIrp->MdlAddress = rwContext->PreviousMdl;
    99 IoFreeMdl(rwContext->NewMdl);
    100
    101 HelloDDKRead_EXIT:
    102 // 完成IRP
    103 pIrp->IoStatus.Status = status;
    104 pIrp->IoStatus.Information = totalLength; // bytes xfered
    105 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    106 KdPrint(("DriverB:Leave B HelloDDKRead\n"));
    107 return status;
    108 }
    中间层驱动,读派遣函数示例代码 P339

    clip_image004

    图示完成例程与派遣例程 P338

    完全例程 示例代码 P342  如上代码示例中

  • 相关阅读:
    人间故事馆话题:聊聊那些被骗经历,让其他人不再被骗
    路过的风景
    路过的风景
    上海最适合拍照的旅游地点
    Java EE (11)
    五、服务器端的局域网
    P1294 高手去散步 洛谷
    堆排序【模板】
    P3383 【模板】线性筛素数 洛谷
    P1516 青蛙的约会 洛谷
  • 原文地址:https://www.cnblogs.com/mydomain/p/1879041.html
Copyright © 2011-2022 走看看