[内核编程] 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的IRP
NTSTATUS 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;
}

 

posted @ 2014-11-02 11:45  .....?  阅读(526)  评论(0编辑  收藏  举报