C++ 上的 SendInput 不显示下划线
Underscores do not get displayed with SendInput On C++
这是我在 Whosebug 上发布的第一个问题。为了让我的程序 'type' 进入另一个程序,我一直在研究 C++ 的 SendInput
。我决定首先在终端中输入带下划线的几个词 'type' 。我发现输入大写和小写字母以及句点没有问题。但是在到达下划线后,输入下划线字母的数字id 95,下划线没有显示,完全就像那个字母从未被按下过一样。这是我从 cplusplus.com 得到的代码,它是我基于的代码,它功能齐全:
#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;
}
这是我 运行 错误的代码:
#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;
}
这会输出 Ninja. Hi123456
而不是 Ninja. Hi123_456
。
其他值得注意的事情:
1).对于 'typed' 期间 ('.'),工作 ID 是 0xBE
而不是 0x3A
.
2).这是使用 Mingw 在 Windows 10 上编译的。
我希望这足够彻底,提前谢谢你!
虚拟键码0x3A
不是句点字符。事实上,per Microsoft's documentation、0x3A
根本就没有定义。对于句点字符,您必须使用 VK_OEM_PERIOD
代替:
VK_OEM_PERIOD
0xBE
For any country/region, the '.' key
也就是说,用 cInputs=1
调用 SendInput()
通常 是一个逻辑错误。当您背靠背发送多个输入事件时,肯定总是一个错误,就像您的示例代码所做的那样。 SendInput()
存在的全部原因是取代 keybd_event()
(和 mouse_event()
),后者一次只能发送一个输入事件。模拟多个事件时,您不希望在事件之间插入其他事件,反之亦然。 SendInput()
与其他输入机制是原子的,但在发送多个事件时,只有在一次发送所有事件时才能保证原子性。
您应该将您的 INPUT
放入数组中并调用 SendInput()
一次,并将 cInputs
设置为您发送的 INPUT
的总数。
此外,当发送文本字符的键输入时,需要使用 VkKeyScan/Ex()
来获得正确的虚拟键码和 shift 状态,尽管 更容易,所以你可以发送实际的 Unicode 字符而不是虚拟键码。
尝试更像这样的东西:
#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;
}
这是我在 Whosebug 上发布的第一个问题。为了让我的程序 'type' 进入另一个程序,我一直在研究 C++ 的 SendInput
。我决定首先在终端中输入带下划线的几个词 'type' 。我发现输入大写和小写字母以及句点没有问题。但是在到达下划线后,输入下划线字母的数字id 95,下划线没有显示,完全就像那个字母从未被按下过一样。这是我从 cplusplus.com 得到的代码,它是我基于的代码,它功能齐全:
#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;
}
这是我 运行 错误的代码:
#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;
}
这会输出 Ninja. Hi123456
而不是 Ninja. Hi123_456
。
其他值得注意的事情:
1).对于 'typed' 期间 ('.'),工作 ID 是 0xBE
而不是 0x3A
.
2).这是使用 Mingw 在 Windows 10 上编译的。
我希望这足够彻底,提前谢谢你!
虚拟键码0x3A
不是句点字符。事实上,per Microsoft's documentation、0x3A
根本就没有定义。对于句点字符,您必须使用 VK_OEM_PERIOD
代替:
VK_OEM_PERIOD
0xBEFor any country/region, the '.' key
也就是说,用 cInputs=1
调用 SendInput()
通常 是一个逻辑错误。当您背靠背发送多个输入事件时,肯定总是一个错误,就像您的示例代码所做的那样。 SendInput()
存在的全部原因是取代 keybd_event()
(和 mouse_event()
),后者一次只能发送一个输入事件。模拟多个事件时,您不希望在事件之间插入其他事件,反之亦然。 SendInput()
与其他输入机制是原子的,但在发送多个事件时,只有在一次发送所有事件时才能保证原子性。
您应该将您的 INPUT
放入数组中并调用 SendInput()
一次,并将 cInputs
设置为您发送的 INPUT
的总数。
此外,当发送文本字符的键输入时,需要使用 VkKeyScan/Ex()
来获得正确的虚拟键码和 shift 状态,尽管
尝试更像这样的东西:
#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;
}