我正在为Windows编写键盘过滤器驱动程序,我需要将自定义击键数据插入Windows消息队列。我已经设法捕获所有按下的键,在我的驱动程序的Read()函数中将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()中按下的所有键。但我需要将自己的信息插入键盘消息流中。这有两个问题:
仅在按下某个键时调用OnReadCompletion()。理想情况下,我想以某种方式在没有按下时调用它。我能以某种方式这样做吗?我需要触发键盘中断吗?我尝试使用WRITE_PORT_UCHAR()将命令写入键盘端口(0x60和0x64)但是没有用完。
我试图将我的数据插入到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 :(得分:1)
我认为应该有四种选择:
1)您可以创建一个新的IRP来调用kbdclass驱动程序,而不是传递您收到的IRP。无论何时想要插入数据以及每次有真正的键击传递,您都可以完成原始IRP。
2)你可以有两个设备,第二个是键盘设备。然后,您将使用kbdclass过滤器删除击键和键盘设备以添加它们。
3)您可以将驱动程序重新设计为键盘设备的上层过滤器,类似于MSDN示例驱动程序kbfiltr。
4)你可以有两个设备,第二个是一个或多个键盘设备的上部过滤器。您可以使用kbdclass过滤器来删除击键,使用键盘设备过滤器来添加它们。
我认为第一种选择是最好的,但我不是专家。