SendInput()没有“发送”正确的按下Shift键后的字符?

7
void WriteChar(char c)
{
    INPUT input = {0};
    input.type = INPUT_KEYBOARD;
    input.ki.wVk= VkKeyScanEx(c, GetKeyboardLayout(0) ) ;   
    SendInput(1,&input, sizeof(INPUT));
}

VkKeyScanEx返回'/'和'?'(同一个键)不同的键码,但是如果你尝试使用这种方法写一个包含'?'的消息,它只会写入'/'。我不知道发生了什么。分号和冒号也是同样的情况。

我部分地不理解键码和扫描码。大多数字符都有虚拟键码,但是我找不到类似问号的东西。它们必须存在,但没有列出来吗?

2个回答

9

扫描码是从键盘返回的原始按键id。因此,一个101键的键盘理论上将有101个独特的扫描码可以返回。(详见脚注1)

虚拟键代码是代表理想键盘上的按键的另一组代码。无论 TAB 键在真实键盘上的位置以及使用什么扫描码,虚拟键代码始终为 VK_TAB。windows.h 为非可打印虚拟键定义了 VK_xxx 代码,对于可打印的虚拟键,虚拟键代码与 ASCII 值相同。

但虚拟键代码仍然是按键代码。'A' 和 'a' 具有相同的虚拟键代码,因此如果要发送一个 'A',则需要发送 VK_SHIFT 按下,然后发送 'a' 按下,之后 'a' 抬起,最后 VK_SHIFT 抬起。

VkKeyScanEx() 将字符转换为扫描键和换挡状态。请参阅以下引用自此页面的内容:http://msdn.microsoft.com/en-us/library/ms646332(VS.85).aspx

如果函数成功,则返回值的低位字节包含虚拟键代码,高位字节包含换挡状态,该状态可以是以下标志位的组合。

因此,您不能仅使用 VkKeyScanEx() 的返回值,需要检查是否有换挡键标记,并将换挡键作为单独的按键操作发送。

SHORT vk = VkKeyScanEx(c, ...);
if (vk & 0x100) // check upper byte for shift flag
{
   // send a shift key down
}
if (vk & 0x200) // check for ctrl flag
{
   // send a ctrl key down
}
input.ki.wVk = vk & 0xFF;

// send keyup for each of the keydown

你还需要在每次按下键时发送一个keyup。
注释:
1. 这只是理论上的,实际上标准PC键盘模拟了一个你甚至无法再获得的旧IBM键盘,因此某些键基于另一个键可以返回2个不同的扫描码,而在其他情况下,两个键可能会返回相同的扫描码。

嗨,谢谢回复。我以为你不需要为每个按键按下执行keyup事件,因为如果你在记事本窗口中测试这个功能,它只会写入一个单一的字符(而如果按住不放,则显然是一个无休止的字符字符串)。为什么我这样假设是错误的? - Alex Gramatikov
当你按住一个键时,键盘实际上会发送 keydown、keydown、keydown、keydown...,并在最终释放它时发送 keyup。唯一的例外是 CTRL、SHIFT 和 ALT,它们不会重复。 - John Knoeller
我认为不是键盘本身反复发送“keydown”信号,而是由计算机控制这种行为。 - Sam
如果是旧款的PS/2键盘,那么就是键盘本身处理重复按键。我不知道USB键盘是怎么处理的,但我猜测是驱动程序处理USB键盘的重复按键。 - John Knoeller
记得发送 VK_RMENU 以便使用右 ALT 键作为字符的键盘组合。 - cen

1

尝试像这样做。如果您使用KEYEVENTF_UNICODE进行操作,则主机平台需要至少是Windows 2000或XP。

INPUT input[ 2 ];

input[ 0 ].type = INPUT_KEYBOARD;
input[ 0 ].ki.wVk = 0;
input[ 0 ].ki.wScan = c;
input[ 0 ].ki.dwFlags = KEYEVENTF_UNICODE;
input[ 0 ].ki.time = 0;
input[ 0 ].ki.dwExtraInfo = GetMessageExtraInfo();

input[ 1 ].type = INPUT_KEYBOARD;
input[ 1 ].ki.wVk = 0;
input[ 1 ].ki.wScan = c;
input[ 1 ].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
input[ 1 ].ki.time = 0;
input[ 1 ].ki.dwExtraInfo = GetMessageExtraInfo();

SendInput( ( UINT )2, input, sizeof( *input ) );

从你没有正确填写输入结构并留下了一些必需的成员未初始化的事实来看,我猜想你还没有看过这个文档。请参阅SendInputINPUTKEYBDINPUT

我经常看到 input.ki.dwExtraInfo 被设置为 0。它应该被设置为 input[ 0 ].ki.dwExtraInfo = GetMessageExtraInfo(); 吗? - Class Skeleton
@rasteve,老实说我也不知道!我可能只是这样做,因为KEYBDINPUT的文档(https://msdn.microsoft.com/en-us/library/ms646271(VS.85).aspx)对于`dwExtraInfo`有这样的描述:`使用GetMessageExtraInfo函数获取此信息。`。 - Sam
我曾经认为使用GetMessageExtraInfo可以获取您在dwExtraInfo中输入的内容,但现在我不太确定了。 - Class Skeleton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接