首页 > 代码库 > 4.3 键盘过滤的请求处理

4.3 键盘过滤的请求处理

4.3 键盘过滤的请求处理

 

4.3.1 通常处理

(1) 最通常的处理就是直接发到真实设备,跳过虚拟设备的处理。这里和前面的串口过滤的方法一样。

NTSTATUS c2pDispatchGeneral(                                  IN PDEVICE_OBJECT DeviceObject,                                  IN PIRP Irp                                  ) {     // 其他的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备    // 的设备对象。     KdPrint(("Other Diapatch!"));     IoSkipCurrentIrpStackLocation(Irp);          return IoCallDriver(((PC2P_DEV_EXT)        DeviceObject->DeviceExtension)->LowerDeviceObject, Irp); }    

注:代码中直接使用了设备扩展。

(2) 与电源相关的IRP处理:
(a)在调用IoSkipCurrentIrpStackLocation之前,先调用PoStartNextPowerIrp。
(b)用PoCallDriver代替IoCallDriver。

//只处理主功能号为IRP_MJ_POWER的IRPNTSTATUS c2pPower(                        IN PDEVICE_OBJECT DeviceObject,                        IN PIRP Irp                        ) {     PC2P_DEV_EXT devExt;    devExt =        (PC2P_DEV_EXT)DeviceObject->DeviceExtension;     PoStartNextPowerIrp( Irp );     IoSkipCurrentIrpStackLocation( Irp );     return PoCallDriver(devExt->LowerDeviceObject, Irp ); } 

注:c2pPower只处理主功能号为IRP_MJ_POWER的IRP;而c2pDispatchGeneral处理我们不关心的所有IRP。


4.3.2 PNP处理

唯一需要处理的就是,当有一个设备被拔出时,则解除绑定,并删除过滤设备。代码的实现如下:

NTSTATUS c2pPnP(                      IN PDEVICE_OBJECT DeviceObject,                      IN PIRP Irp                      ) {     PC2P_DEV_EXT devExt;     PIO_STACK_LOCATION irpStack;     NTSTATUS status = STATUS_SUCCESS;     KIRQL oldIrql;     KEVENT event;     // 获得真实设备。    devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension);     irpStack = IoGetCurrentIrpStackLocation(Irp);     switch (irpStack->MinorFunction)     {     case IRP_MN_REMOVE_DEVICE:         KdPrint(("IRP_MN_REMOVE_DEVICE\n"));         // 首先把请求发下去        IoSkipCurrentIrpStackLocation(Irp);         IoCallDriver(devExt->LowerDeviceObject, Irp);         // 然后解除绑定。        IoDetachDevice(devExt->LowerDeviceObject);         // 删除我们自己生成的虚拟设备。        IoDeleteDevice(DeviceObject);         status = STATUS_SUCCESS;         break;     default:         // 对于其他类型的IRP,全部都直接下发即可。         IoSkipCurrentIrpStackLocation(Irp);         status = IoCallDriver(devExt->LowerDeviceObject, Irp);     }     return status; }

 

 

当PNP请求过来时,是没有必要担心还有未完成的IRP的。这是因为Windows系统要求卸载设备,此时Windows自己应该已经处理掉了所有未决的IRP。这是和我们自己要求卸载过滤驱动不同的地方。


4.3.3 读处理

前面章节中,见到的请求,都是处理完毕后,直接发送到下层驱动之后就不管了。但在处理键盘请求时,就不行。

改变后的处理方式:先把这个请求下发完之后,再去看这个键盘扫描码的值是多少。要完成请求,可采用以下步骤:
(1) 调用IoCopyCurrentIrpStackLocationToNext 把当前IRP栈空间拷贝到下一个栈空间(这和前面的调用IoSkiCurrentIrpStackLocation跳过当前栈空间形成对比)。
(2) 给这个IRP设置一个完成函数。完成函数的含义是:如果这个IRP完成了,系统会回调这个函数。
(3) 调用IoCallDriver把请求发送到下一个设备。

另外一个需要解决的问题就是键计数器的处理。即请求到来时,gC2pKeyCount加 1,等完成之后再减 1。

完整的读处理请求如下:

NTSTATUS c2pDispatchRead(                               IN PDEVICE_OBJECT DeviceObject,                               IN PIRP Irp ) {     NTSTATUS status = STATUS_SUCCESS;     PC2P_DEV_EXT devExt;     PIO_STACK_LOCATION currentIrpStack;     KEVENT waitEvent;    KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );    if (Irp->CurrentLocation == 1)     {         ULONG ReturnedInformation = 0;         KdPrint(("Dispatch encountered bogus current location\n"));         status = STATUS_INVALID_DEVICE_REQUEST;         Irp->IoStatus.Status = status;         Irp->IoStatus.Information = ReturnedInformation;         IoCompleteRequest(Irp, IO_NO_INCREMENT);         return(status);     }     // 全局变量键计数器加1    gC2pKeyCount++;    // 得到设备扩展。目的是之后为了获得下一个设备的指针。    devExt =        (PC2P_DEV_EXT)DeviceObject->DeviceExtension;    // 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。    // 剩下的任务是要等待读请求完成。    currentIrpStack = IoGetCurrentIrpStackLocation(Irp);     IoCopyCurrentIrpStackLocationToNext(Irp);    IoSetCompletionRoutine( Irp, c2pReadComplete,         DeviceObject, TRUE, TRUE, TRUE );     return  IoCallDriver( devExt->LowerDeviceObject, Irp );     }

 

4.3.4 读完成的处理

读请求完成之后,应该获得输出缓冲区,按键信息就在输出缓冲区中,全局变量gC2pKeyCount应该减 1 。

NTSTATUS c2pReadComplete(                               IN PDEVICE_OBJECT DeviceObject,                               IN PIRP Irp,                               IN PVOID Context                               ) {     PIO_STACK_LOCATION IrpSp;     ULONG buf_len = 0;     PUCHAR buf = NULL;     size_t i,numKeys;     PKEYBOARD_INPUT_DATA KeyData;      IrpSp = IoGetCurrentIrpStackLocation( Irp );     //  如果这个请求是成功的。很显然,如果请求失败了,这么获取     //   进一步的信息是没意义的。     if( NT_SUCCESS( Irp->IoStatus.Status ) )      {        // 获得读请求完成后输出的缓冲区        buf = Irp->AssociatedIrp.SystemBuffer;        KeyData = (PKEYBOARD_INPUT_DATA)buf;        // 获得这个缓冲区的长度。一般的说返回值有多长都保存在        // Information中。        buf_len = Irp->IoStatus.Information;        numKeys = buf_len / sizeof(KEYBOARD_INPUT_DATA);        //… 这里可以做进一步的处理。我这里很简单的打印出所有的扫        // 描码。        //for(i=0;i<buf_len;++i)        for(i=0;i<numKeys;++i)        {            //DbgPrint("ctrl2cap: %2x\r\n", buf[i]);            DbgPrint("\n");            DbgPrint("numKeys : %d",numKeys);            DbgPrint("ScanCode: %x ", KeyData->MakeCode );             DbgPrint("%s\n", KeyData->Flags ?"Up" : "Down" );            print_keystroke((UCHAR)KeyData->MakeCode);            if( KeyData->MakeCode == CAPS_LOCK)             {                 KeyData->MakeCode = LCONTROL;             }         }    }    gC2pKeyCount--;    if( Irp->PendingReturned )    {         IoMarkIrpPending( Irp );     }     return Irp->IoStatus.Status;}

 

4.3 键盘过滤的请求处理