Search code examples
c++keystrokesendinputmodifier-key

c++ - SendInput() doesn't manage Alt codes properly


On a program that I'm developing I have to simulate keystrokes, and to do so I use the SendInput() method, passing as argument a vector containing the inputs which are part of the keystroke. My current code seems to work properly with all the combinations I'm testing, except Alt codes.

This is what I do currently:

// Press ALT
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_LMENU;
input.ki.wScan = 0;
input.ki.dwFlags = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Press NumPad2
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD2;
input.ki.wScan = 0;
input.ki.dwFlags = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Release NumPad2
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD2;
input.ki.wScan = 0;
input.ki.dwFlags = KEYEVENTF_KEYUP;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Press NumPad1
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD1;
input.ki.wScan = 0;
input.ki.dwFlags = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Release NumPad1
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD1;
input.ki.wScan = 0;
input.ki.dwFlags = KEYEVENTF_KEYUP;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Press NumPad2
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD2;
input.ki.wScan = 0;
input.ki.dwFlags = 0;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Release NumPad2
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_NUMPAD2;
input.ki.wScan = 0;
input.ki.dwFlags = KEYEVENTF_KEYUP;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

// Release ALT
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_LMENU;
input.ki.wScan = 0;
input.ki.dwFlags = KEYEVENTF_KEYUP;
input.ki.time = 0;
input.ki.dwExtraInfo = 0;    
keystroke.push_back(input);

SendInput(keystroke.size(), &keystroke[0], sizeof(keystroke[0]));

The push_backs are done in a for cycle, that's why I entirely redefine the input variable everytime.

This approach seems to work for every combination excluding Alt codes. How can I make them work too? Thank you.

PS: As you can notice, dwFlags never declares ALT (VK_LMENU) as an ExtendedKey since from my understanding only VK_RMENU (and not VK_LMENU) is such. This MSDN page seems to confirm so.


Solution

  • Use scan codes instead of virtual keys. That injects keys a much lower level into the system and simulates real user typing more reliably than Virtual Keys.

    It took me a while to find the definitive list of scan codes since there's some variances out there. But referencing the "set 1" column from the big table in the middle of this page seemed to work.

    INPUT createScanCodeEvent(WORD scancode, bool isDown)
    {
        INPUT input = {};
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = 0;
        input.ki.wScan = scancode;
        input.ki.dwFlags = (isDown ? 0 : KEYEVENTF_KEYUP) | KEYEVENTF_SCANCODE;
        input.ki.time = 0;
        input.ki.dwExtraInfo = 0;
        return input;
    }
    
    int inject()
    {
        std::vector<INPUT> keystroke;
        const WORD SCANCODE_ALT = 0x38;
        const WORD SCANCODE_NUMPAD_1 = 0x4f;
        const WORD SCANCODE_NUMPAD_2 = 0x50;
    
        keystroke.push_back(createScanCodeEvent(SCANCODE_ALT, true) );
    
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_2, true));
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_2, false));
    
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_1, true));
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_1, false));
    
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_2, true));
        keystroke.push_back(createScanCodeEvent(SCANCODE_NUMPAD_2, false));
    
        keystroke.push_back(createScanCodeEvent(SCANCODE_ALT, false));
    
        SendInput(keystroke.size(), keystroke.data(), sizeof(keystroke[0]));
    
        return 0;
    }