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.