This is my first question I have posted on stackoverflow. I have been looking into SendInput
for C++ in order to have my program 'type' into another program. I decided to start out by having it 'type' in a couple words with underscores into the terminal. I found no problem having it type upper and lowercase letters, as well as a period. But after getting to the underscore, typing in the number id 95 for the underscore letter, the underscore did not display, and acted completely like that letter was never pressed. Here is the code that I got off of cplusplus.com to that I based it off of, it is fully functional:
#include <iostream>
#include <windows.h>
using namespace std;
/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(0, "Command Prompt");
/* This is a function to simplify usage of sending keys */
void GenerateKey(int vk, BOOL bExtended) {
KEYBDINPUT kb = {0};
INPUT Input = {0};
/* Generate a "key down" */
if (bExtended) { kb.dwFlags = KEYEVENTF_EXTENDEDKEY; }
kb.wVk = vk;
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
SendInput(1, &Input, sizeof(Input));
/* Generate a "key up" */
ZeroMemory(&kb, sizeof(KEYBDINPUT));
ZeroMemory(&Input, sizeof(INPUT));
kb.dwFlags = KEYEVENTF_KEYUP;
if (bExtended) { kb.dwFlags |= KEYEVENTF_EXTENDEDKEY; }
kb.wVk = vk;
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
SendInput(1, &Input, sizeof(Input));
return;
}
int main() {
/*
SetForegroundWindow will give the window focus for the
keyboard/mouse! In other words, you don't have to have
the game opened upfront in order to emulate key/mouse
presses, it's very helpful if it's a game that runs
in fullscreen mode, like StarCraft: Brood War does
*/
SetForegroundWindow(GameWindow);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('I', FALSE);
GenerateKey(' ', FALSE);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('A', FALSE);
GenerateKey('M', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('C', FALSE);
GenerateKey('O', FALSE);
GenerateKey('O', FALSE);
GenerateKey('L', FALSE);
GenerateKey('E', FALSE);
GenerateKey('R', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('T', FALSE);
GenerateKey('H', FALSE);
GenerateKey('A', FALSE);
GenerateKey('N', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('Y', FALSE);
GenerateKey('O', FALSE);
GenerateKey('U', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('W', FALSE);
GenerateKey('I', FALSE);
GenerateKey('L', FALSE);
GenerateKey('L', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('E', FALSE);
GenerateKey('V', FALSE);
GenerateKey('E', FALSE);
GenerateKey('R', FALSE);
GenerateKey(' ', FALSE);
GenerateKey('B', FALSE);
GenerateKey('E', FALSE);
GenerateKey('n', FALSE);
GenerateKey(' ', FALSE);
GenerateKey(0x3A, FALSE); /* period key */
GenerateKey(0x0D, FALSE); /* enter key */
return 0;
}
And this is the code that I made that ran incorrectly:
#include <iostream>
#include <fstream>
#include <windows.h>
using namespace std;
/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(0, "Command Prompt");
/* This is a function to simplify usage of sending keys */
void GenerateKey(int vk, BOOL bExtended) {
KEYBDINPUT kb = {0};
INPUT Input = {0};
/* Generate a "key down" */
if (bExtended) { kb.dwFlags = KEYEVENTF_EXTENDEDKEY; }
kb.wVk = vk;
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
SendInput(1, &Input, sizeof(Input));
/* Generate a "key up" */
ZeroMemory(&kb, sizeof(KEYBDINPUT));
ZeroMemory(&Input, sizeof(INPUT));
kb.dwFlags = KEYEVENTF_KEYUP;
if (bExtended) { kb.dwFlags |= KEYEVENTF_EXTENDEDKEY; }
kb.wVk = vk;
Input.type = INPUT_KEYBOARD;
Input.ki = kb;
SendInput(1, &Input, sizeof(Input));
return;
}
int main() {
/*
SetForegroundWindow will give the window focus for the
keyboard/mouse! In other words, you don't have to have
the game opened upfront in order to emulate key/mouse
presses, it's very helpful if it's a game that runs
in fullscreen mode, like StarCraft: Brood War does
*/
SetForegroundWindow(GameWindow);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('N', FALSE);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('I', FALSE);
GenerateKey('N', FALSE);
GenerateKey('J', FALSE);
GenerateKey('A', FALSE);
GenerateKey(0xBE, FALSE); // GenerateKey(0x3A, FALSE); did not work
GenerateKey(' ', FALSE);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('H', FALSE);
GenerateKey(VK_CAPITAL, TRUE);
GenerateKey('I', FALSE);
GenerateKey('1', FALSE);
GenerateKey('2', FALSE);
GenerateKey('3', FALSE);
GenerateKey(95 , FALSE); // GenerateKey('_', FALSE); did not work either
GenerateKey('4', FALSE);
GenerateKey('5', FALSE);
GenerateKey('6', FALSE);
return 0;
}
This outputs Ninja. Hi123456
instead of Ninja. Hi123_456
.
Other things worthy of note:
1). For the period ('.') being 'typed' out the working id was 0xBE
instead of 0x3A
.
2). This was compiled on Windows 10 using Mingw.
I hope this was thorough enough, thank you in advance!
Virtual Key Code 0x3A
is not a period character. In fact, per Microsoft's documentation, 0x3A
is NOT EVEN DEFINED at all. For a period character, you must use VK_OEM_PERIOD
instead:
VK_OEM_PERIOD
0xBEFor any country/region, the '.' key
That being said, calling SendInput()
with cInputs=1
is usually a logic bug. Certainly ALWAYS a bug when you are sending multiple input events back-to-back, as your example code is doing. The whole reason SendInput()
exists at all is to replace keybd_event()
(and mouse_event()
), which can only send one input event at a time. When simulating multiple events, you don't want other events getting injected in between your events, and vice versa. SendInput()
is atomic with other input mechanisms, but when sending multiple events, that atomicity is only guaranteed when you send all of your events at one time.
You should put your INPUT
s into an array and call SendInput()
ONCE, with cInputs
set to the total number of INPUT
s you are sending.
Also, when sending key input for text characters, use need to use VkKeyScan/Ex()
to get the correct virtual key code and shift state, though it is a lot easier to use the KEYEVENTF_UNICODE
flag instead so you can send actual Unicode characters instead of virtual key codes.
Try something more like this instead:
#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(NULL, TEXT("Command Prompt"));
void GenerateKeyDown(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
INPUT in = {};
in.type = INPUT_KEYBOARD;
in.ki.wVk = vk;
if (bExtended) in.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
inputQueue.push_back(in);
}
void GenerateKeyUp(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
INPUT in = {};
in.type = INPUT_KEYBOARD;
in.ki.wVk = vk;
if (bExtended) in.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
in.ki.dwFlags |= KEYEVENTF_KEYUP;
inputQueue.push_back(in);
}
void GenerateKey(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
INPUT in[2] = {};
in[0].type = INPUT_KEYBOARD;
in[0].ki.wVk = vk;
if (bExtended) in[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
in[1] = in[0];
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
inputQueue.insert(inputQueue.end(), in, in+1);
}
void GenerateString(std::vector<INPUT> &inputQueue, const std::wstring &str)
{
int len = str.length();
if (len == 0) return;
inputQueue.reserve(inputQueue.size()+(len*2));
INPUT in = {};
in.type = INPUT_KEYBOARD;
in.ki.dwFlags = KEYEVENTF_UNICODE;
int i = 0;
while (i < len)
{
WORD ch = (WORD) str[i++];
if ((ch < 0xD800) || (ch > 0xDFFF))
{
in.ki.wScan = ch;
in.ki.dwFlags &= ~KEYEVENTF_KEYUP;
inputQueue.push_back(in);
in.ki.dwFlags |= KEYEVENTF_KEYUP;
inputQueue.push_back(in);
}
else
{
WORD ch2 = (WORD) str[i++];
in.ki.wScan = ch;
in.ki.dwFlags &= ~KEYEVENTF_KEYUP;
inputQueue.push_back(in);
in.ki.wScan = ch2;
inputQueue.push_back(in);
in.ki.wScan = ch;
in.ki.dwFlags |= KEYEVENTF_KEYUP;
inputQueue.push_back(in);
in.ki.wScan = ch2;
inputQueue.push_back(in);
}
}
}
int main()
{
/*
SetForegroundWindow will give the window focus for the
keyboard/mouse! In other words, you don't have to have
the game opened upfront in order to emulate key/mouse
presses, it's very helpful if it's a game that runs
in fullscreen mode, like StarCraft: Brood War does
*/
SetForegroundWindow(GameWindow);
std::vector<INPUT> inputQueue;
/*
GenerateString(inputQueue, L"I Am cooler than you will ever ben .");
GenerateKey(inputQueue, VK_RETURN);
*/
GenerateString(inputQueue, L"NInja. HI123_456");
/* alternatively:
GenerateString(inputQueue, L"NInja");
GenerateKey(inputQueue, VK_OEM_PERIOD);
GenerateString(inputQueue, L" HI123");
// see why using KEYEVENTF_UNICODE is easier?
SHORT ret = VkKeyScanW(L'_');
BYTE vk = LOBYTE(ret);
BYTE shift = HIBYTE(ret);
if (vk != -1)
{
SHORT state = GetKeyState(VK_SHIFT);
bool bIsDown = (state & 0x800);
if (shift & 1)
{
if (!bIsDown)
GenerateKeyDown(inputQueue, VK_SHIFT);
}
else
{
if (bIsDown)
GenerateKeyUp(inputQueue, VK_SHIFT);
}
GenerateKey(inputQueue, vk);
if (shift & 1)
{
if (!bIsDown)
GenerateKeyUp(inputQueue, VK_SHIFT);
}
else
{
if (bIsDown)
GenerateKeyDown(inputQueue, VK_SHIFT);
}
}
GenerateString(inputQueue, L"456");
*/
SendInput(inputQueue.size(), &inputQueue[0], sizeof(INPUT));
return 0;
}