2011-09-01 18 views
5

我正在为Windows编写键盘过滤器驱动程序,我需要将我的自定义键击数据插入到Windows消息队列中。我已经成功地捕捉到所有被压在我的驾驶读(设置OnReadCompletion()回调至IoSetCompletionRoutine函数())函数,像这样的按键:如何触发或模拟键盘中断?

NTSTATUS Read(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 
{ 
    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; 

    IoCopyCurrentIrpStackLocationToNext(Irp); 
    IoSetCompletionRoutine(Irp, OnReadCompletion, DeviceObject, TRUE, TRUE, TRUE); 
    return IoCallDriver (deviceExtension->pKeyboardDevice, Irp); 
} 

NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) 
{ 
// ... 
} 

该过滤器驱动程序安装到kbdclass驱动程序,如下所示:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) 
{ 
// ... 
CCHAR ntNameBuffer[64] = "\\Device\\KeyboardClass0"; 
status = IoAttachDevice(deviceObject, &uKeyboardDeviceName, &DeviceExtension->pKeyboardDevice); 
// ... 

}

所以,我能赶上在OnReadCompletion按下所有键()。但我需要将自己的信息插入到键盘消息流中。这里有两个问题:

  1. OnReadCompletion()仅在按下按键时调用。理想情况下,我想以某种方式在没有任何按下时调用它。我能以某种方式做到吗?我需要触发键盘中断?我尝试使用WRITE_PORT_UCHAR()将命令写入键盘端口(0x60和0x64),但没有成功。

  2. 我试图将我的数据插入到OnReadCompletion()中的IRP中,使其看起来像一个键被按下两次,而实际上它只被按下一次。有人可以帮我解决这个问题吗,因为以下方面没有解决?

    NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) 
    { 
        PIO_STACK_LOCATION IrpStackLocation = NULL; 
        INT BufferLength; 
        INT numKeys = 0, i = 0; 
        PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; 
    
        IrpStackLocation = IoGetCurrentIrpStackLocation(Irp); 
        BufferLength = IrpStackLocation->Parameters.Read.Length; 
    
        if(Irp->IoStatus.Status == STATUS_SUCCESS) 
        { 
         PCHAR newSystemBuffer, oldSystemBuffer; 
         PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; 
         numKeys = Irp->IoStatus.Information/sizeof(KEYBOARD_INPUT_DATA); 
         for(i = 0; i < numKeys; i++) 
         { 
           // here we print whatever was pressed 
           DbgPrint("%s -- ScanCode: %x\n", __FUNCTION__, keys[i].MakeCode); 
         } 
         // allocate new buffer twice as big as original 
         newSystemBuffer = ExAllocatePool(NonPagedPool, Irp->IoStatus.Information * 2); 
         // copy existing buffer twice into new buffer 
          RtlCopyMemory(newSystemBuffer, keys, Irp->IoStatus.Information); 
         RtlCopyMemory(newSystemBuffer + Irp->IoStatus.Information, keys, Irp->IoStatus.Information); 
          // assign new buffer to Irp->AssociatedIrp.SystemBuffer 
         oldSystemBuffer = Irp->AssociatedIrp.SystemBuffer; 
         Irp->AssociatedIrp.SystemBuffer = newSystemBuffer; 
          // tell IRP that we now have twice as much data 
         Irp->IoStatus.Information *= 2; 
         // free the old buffer 
          ExFreePool(oldSystemBuffer); 
        } 
    
        if(Irp->PendingReturned) 
         IoMarkIrpPending(Irp); 
    
        return Irp->IoStatus.Status; 
    } 
    

当我测试它例如在记事本中,我得到的只是每一个按键字母。 我真的很绝望。请帮忙!

+0

我设法将新数据添加到IRP系统缓冲区。诀窍是使用不同的关键笔划代码,不像上面的代码中那样。所以如果你想插入一个MakeCode = 2(等于按下按钮“1”),在OnReadCompletion()中执行以下操作:RtlCopyMemory(newSystemBuffer,keys,Irp-> IoStatus.Information); keys-> MakeCode = 2; RtlCopyMemory(newSystemBuffer + Irp-> IoStatus.Information,keys,Irp-> IoStatus.Information); –

+0

区别在于“keys-> MakeCode = 2;”这会在第二个KEYBOARD_INPUT_DATA消息中更改MakeCode。在每次击键之后,例如在记事本中,你会得到一个“1”。 –

+0

所以唯一的问题是如何触发中断? –

回答

1

四个选项,我认为应该工作:

1)你可以创建一个新的IRP调用与kbdclass驱动程序,而不是通过您收到下来IRP。无论何时您想要插入数据,以及每当您有真正的击键传递时,您都可以完成原始的IRP。

2)你可以有两个设备,第二个是键盘设备。然后,您将使用kbdclass过滤器来删除键击和用于添加它们的键盘设备。

3)您可以将您的驱动程序重新设计为键盘设备的上级过滤器,类似于MSDN示例驱动程序kbfiltr。

4)您可以有两个设备,第二个是一个或多个键盘设备的上层过滤器。您可以使用kbdclass过滤器来删除击键,并使用键盘设备过滤器来添加它们。

我认为第一个选择是最好的,但我不是专家。

+0

感谢您的回复,队友!但是,如果我理解术语,我的驱动程序就是过滤驱动程序。它正是Kbdfiltr所做的。我发现你有两个选项来修改过滤器驱动程序中的IRP。首先是操作系统对键盘驱动程序堆栈进行读操作。该读取被您的驱动程序捕获。您可以立即回复,从而将按键发送到从未按下的操作系统。如果你不这样做,这个读取会到达堆栈的底部并在那里被阻塞直到用户按下一个键。然后IRP向上移动堆栈,并可以被驱动程序再次捕获并修改。 –

+0

所以你可以两次修改IRP。但是当它被堆叠下的司机阻挡时,你什么也不能做,只能等到它回来。那就是问题所在。因为如果我通过ioclt获得了需要插入的数据,我需要等待按键才能有机会将其插入到IRP中。 –

+0

至于你关于另一个键盘的第二个想法 - 你的意思是实际上将第二个键盘连接到电脑,或者只是为一个不存在的设备制作过滤器驱动程序,如\\ Device \\ KeyboardClass1? –