zoukankan      html  css  js  c++  java
  • [内核编程] 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
    ")); 
    
            // 首先把请求发下去
            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
    ")); 
            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
    ", buf[i]);
                DbgPrint("
    ");
                DbgPrint("numKeys : %d",numKeys);
                DbgPrint("ScanCode: %x ", KeyData->MakeCode ); 
                DbgPrint("%s
    ", 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;
    }
  • 相关阅读:
    斯特林数及斯特林反演
    关于斯特林数的应用总结
    向量运算与几何意义
    linux shell——md5sum,sha1sum,sort,uniq (转)
    hudson配置教程
    linux下安装tomcat和部署web应用
    Tomcat Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
    《Ant权威指南》笔记(一)
    ant调用shell命令(Ubuntu)
    hudson--ant编写记录
  • 原文地址:https://www.cnblogs.com/fanling999/p/4068918.html
Copyright © 2011-2022 走看看