How to properly send keystrokes to system when simulating an HID keyboard (KMDF)

Hi all,

I am fairly new to driver development and I was wondering if someone could help me out with the proper procedure to send keystrokes to the system from a custom KMDF driver. I have a device that I want to act as a mouse and a keyboard so that I can send key strokes to activate general shortcuts such as alt+tab (and other system shortcuts) as well as AC and AL shortcuts. I got the mouse part to work yesterday and I was hoping that I could send keystrokes using the same code as the mouse report function provided that I change the input report structure to be a keyboard input structure that matches what is described in my keyboard TLC. I started from the HidUsbFx2 sample driver. When I receive an IOCTL_HID_READ_REPORT in my internal device control function I forward it to a queue using the following line of code:

status = WdfRequestForwardToIoQueue(Request, devContext->InterruptMsgQueue);

I have copy & pasted the function that I hope to use to report keystrokes as well as the keyboard TLC in my report descriptor and my keyboard input structure below.

TLC IN REPORT DESCRIPTOR:

// Keyboard Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_KEYBOARD, // REPORT_ID (0x02)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (0)
0x29, 0x65, // USAGE_MAXIMUM (101)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION

KEYBOARD INPUT STRUCTURE:

typedef struct _HID_KEYBOARD_REPORT
{
UCHAR reportID;
UCHAR modifierKeys;
UCHAR reserved;
UCHAR key[6];
}HID_KEYBOARD_REPORT, *PHID_KEYBOARD_REPORT;

FUNCTION TO REPORT KEYSTROKES:

VOID Gh_HidKeyboardReport(PDEVICE_EXTENSION devContext, UCHAR eventID){
PHID_KEYBOARD_REPORT report;
NTSTATUS status;
WDFREQUEST request;
int i;

DbgPrint(“Gh_HidKeyboardReport”);

status = WdfIoQueueRetrieveNextRequest(devContext->InterruptMsgQueue, &request);
//status = WdfIoQueueRetrieveNextRequest(devContext->ReadRequestQueue, &request);
if(!NT_SUCCESS(status)){
DbgPrint(“WdfIoQueueRetrieveNextRequest failed with status: 0x%x\n”,status);
}else if (NT_SUCCESS(status)){
//
// IOCTL_HID_READ_REPORT is METHOD_NEITHER so WdfRequestRetrieveOutputBuffer
// will correctly retrieve buffer from Irp->UserBuffer. Remember that
// HIDCLASS provides the buffer in the Irp->UserBuffer field
// irrespective of the ioctl buffer type. However, framework is very
// strict about type checking. You cannot get Irp->UserBuffer by using
// WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER
// internal ioctl.
//

status = WdfRequestRetrieveOutputBuffer(request,
sizeof(HID_KEYBOARD_REPORT),
&report,
NULL); //bufferLength

if (!NT_SUCCESS(status)) { // should never happen
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,
“WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n”, status);
DbgPrint((“Retrieve error\n”));
WdfRequestComplete(request, status);
}else{
report->reportID = REPORTID_KEYBOARD;

if(eventID == KEY_RELEASE){
report->modifierKeys = 0x00;
for(i=0; i<6; i++){
key[i] = 0x00;
}
}else if(eventID == PUSH_BUTTON){
report->modifierKeys = KEY_ALT;
//report->reserved = 0x00;
report->key[0] = KEY_TAB;
report->key[1] = 0x00;
report->key[2] = 0x00;
report->key[3] = 0x00;
report->key[4] = 0x00;
report->key[5] = 0x00;
}

DbgPrint("
Keyboard Report sent: rpID: %d, modifiers = 0x%x, reserved = %d, key1 = %d, key2 = %d, key3 = %d, key4 = %d, key5 = %d, key6 = %d\n", report->reportID, report->modifierKeys, report->reserved, report->key[0], report->key[1], report->key[2], report->key[3], report->key[4], report->key[5]);

WdfRequestCompleteWithInformation(request,status,sizeof(HID_KEYBOARD_REPORT));
// WdfRequestComplete(request, status);
}
}
}

Can someone tell me if I’m on the right track or if I am just way out in left field with this? And if I am way off, can you point me in the right direction?

I also am having an issue where my mouse input only works if I comment out the keyboard TLC and vice versa (WdfRequestRetrieveOutputBuffer fails with status 0x8000001a if I leave both TLC’s in the descriptor). Any ideas on why this is happening? I’d like for them to work together. Thanks in advance, I very much appreciate the help.

xxxxx@gmail.com wrote:

I am fairly new to driver development and I was wondering if someone could help me out with the proper procedure to send keystrokes to the system from a custom KMDF driver. I have a device that I want to act as a mouse and a keyboard so that I can send key strokes to activate general shortcuts such as alt+tab (and other system shortcuts) as well as AC and AL shortcuts.

What kind of a device is this? You can use SendInput from user-mode to
inject mouse and keyboard activity into the HID system without using a
HID driver at all. It would be easier, for example, to have an
application use WinUSB to talk to a custom USB device and translate that
to SendInput than it would to write a KMDF driver.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

It is essentially a multi-touch device with some other functionality built into it (a ring encoder around the outside of the touch pad which can also be pushed in as a push button). The company that I am interning for makes the device and would like me to finish the driver that a previous employee was working on for it. They want the touch pad to function as a pointer/mouse and they want me to map certain gestures and other events (such as pressing the push button) to specific keyboard shortcuts. They want it to support plug-n-play so that it can be used easily with Windows and they want customers to be able to develop their own applications around its functionality. So what I’m currently trying to do is this: when certain events/gestures happen the driver will send out specific keystrokes that the customers can create macros for in their application to allow for custom functionality.