I have written a program to use an external (wireless) numpad as an input device for gaming. I am using NUMPAD0, NUMPAD. and ENTER for the modifier keys shift, ctrl and alt respectively and I've keymapped the rest of the numpad keys to WASD and some other keys. I've setup a LowLevelKeyboardProc to intercept and eat the "real" input of the numpad and send a custom WM_MESSAGE to my application's Windows Message loop to then send the keymapped "new" input via SendInput (using the scan codes of the keys I want to simulate).
This all works fine and as expected. Except for the left shift key: Whenever I press NUMPAD0, which is mapped to left shift, not only the correct scan code 0x2A = 42 is send, but also another key with scan code 0x022a = 554. This seems like some sort of flag in bit #9, since 554 is 42 + 2^9, but I can find any documentation on this. I've read about extended 2-byte scan codes with prefix 0xE0, but not 0x02 as in this case. This also only happens with the simulated shift key; ctrl and alt a behaving just fine. Relevant parts of both the keyboard hook and the windows messaging stuff:
LowLevelKeyboardProc:
LPWSTR convertToHex(LPBYTE in, size_t size_in_Bytes)
{
std::stringstream tempS;
tempS << std::hex;
for(int i = 0; i<size_in_Bytes; i++)
{
tempS << std::setfill('0') << std::setw(2) << int(in[i]) << " ";
}
std::string tempString = tempS.str();
std::wstring tempW(tempString.begin(), tempString.end());
LPWSTR out= (LPWSTR) malloc((tempW.size()+2)* sizeof(WCHAR));
StringCbCopyW(out, tempW.size()* sizeof(WCHAR), tempW.c_str());
return out;
}
LRESULT CALLBACK LowLevelKeyboardProc(__in int nCode, __in WPARAM wParam, __in LPARAM lParam)
{
KBDLLHOOKSTRUCT *key=(KBDLLHOOKSTRUCT *)lParam;
wchar_t tempString[128];
LPWSTR hexOut = convertToHex((LPBYTE) key , sizeof(KBDLLHOOKSTRUCT));
StringCbPrintfW(tempString, sizeof(tempString), L"HEX: %s \n", hexOut);
OutputDebugStringW(tempString);
free(hexOut);
//if(key->scanCode > 0xFF)
// return -1; //ignore strange scancode
if(nCode==HC_ACTION)
{
if(isNumpad)
{
auto it = KeyMap.find(key->scanCode);
if(it != KeyMap.end())
{
PostMessage(hwndMain, WM_MY_KEYDOWN, (key->flags & 128), it->first);
StringCbPrintfW(tempString, sizeof(tempString), L"Intercept Key, VK: %d, scan: %d, flags: %d, time: %d, event: %d \n", key->vkCode, key->scanCode, key->flags, key->time, wParam);
OutputDebugStringW(tempString);
return -1;
}
}
}
// delete injected flag
//UINT mask = (1<<4);
//if(key->flags & mask)
// key->flags ^= mask;
//key->scanCode = MapVirtualKey(key->vkCode, MAPVK_VK_TO_VSC);
StringCbPrintfW(tempString, sizeof(tempString), L"Output key: VK: %d, scan: %d, flags: %d, time: %d, event: %d \n", key->vkCode, key->scanCode, key->flags, key->time, wParam);
OutputDebugStringW(tempString);
return CallNextHookEx(hhkHook,nCode,wParam,lParam);
}
Windows Message stuff:
case WM_MY_KEYDOWN:
{
DWORD OLD_KEY = lParam;
DWORD NEW_KEY = KeyMap[OLD_KEY];
bool keyUP = wParam;
INPUT inputdata;
inputdata.type=INPUT_KEYBOARD;
inputdata.ki.dwFlags=KEYEVENTF_SCANCODE ;
inputdata.ki.wScan=NEW_KEY;
inputdata.ki.wVk=MapVirtualKey(NEW_KEY, MAPVK_VSC_TO_VK);
inputdata.ki.time = 0;//key->time;
inputdata.ki.dwExtraInfo = 0;//key->dwExtraInfo;
if(keyUP)
{
inputdata.ki.dwFlags |= KEYEVENTF_KEYUP;
}
Sleep(1);
bool sendKey=false;
switch(inputdata.ki.wScan)
{
//there once was some additional logic here to handle the modifier keys ctrl, alt and shift seperately, but not anymore
default:
{
sendKey=true;
}
break;
}
if (sendKey)
{
UINT result = SendInput(1, &inputdata, sizeof(INPUT));
if(!result)
ErrorExit(L"SendInput didn't work!");
}
}
break;
Output when pressing the normal shift key on a physical keyboard:
//key down
HEX: a0 00 00 00 2a 00 00 00 00 00 00 00 29 cf d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 42, flags: 0, time: 13815593, event: 256
//key up
HEX: a0 00 00 00 2a 00 00 00 80 00 00 00 e5 cf d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 42, flags: 128, time: 13815781, event: 257
Output when pressing the simulated shift key on the numpad:
//keydown event numpad
HEX: 60 00 00 00 52 00 00 00 00 00 00 00 a1 ea d2 00 00 00 00 00 00 00 00 00
Intercept Key, VK: 96, scan: 82, flags: 0, time: 13822625, event: 256
//keydown event simulated shift key
HEX: a0 00 00 00 2a 00 00 00 10 00 00 00 a1 ea d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 42, flags: 0, time: 13822625, event: 256
// key up event with strange scancode 554 <- THIS IS WHAT I AM TALKING ABOUT!
HEX: a0 00 00 00 2a 02 00 00 80 00 00 00 3d eb d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 554, flags: 128, time: 13822781, event: 257
// key up event numpad
HEX: 2d 00 00 00 52 00 00 00 80 00 00 00 3d eb d2 00 00 00 00 00 00 00 00 00
Intercept Key, VK: 45, scan: 82, flags: 128, time: 13822781, event: 257
// key down with strange scan code
HEX: a0 00 00 00 2a 02 00 00 00 00 00 00 3d eb d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 554, flags: 0, time: 13822781, event: 256
//key up event simulated shift key
HEX: a0 00 00 00 2a 00 00 00 90 00 00 00 3d eb d2 00 00 00 00 00 00 00 00 00
Output key: VK: 160, scan: 42, flags: 128, time: 13822781, event: 257
Another thing to note is that the key-up event of this strange key scan code 554 is also exactly in reverse to what the rest is (see output). As I can simply eat the key event for this strange scan code 554, this is actually not much of a problem functionality-wise, but I would still like to know what this scan code 554 is all about.