SendInput无法使用打印屏幕键。

8
我正在为各种模拟器制作前端,并触发它们的各种功能,例如保存/加载状态、保存截图,但使用统一界面。FS-UAE令人讨厌地使用“Print Screen”作为其截图键,我想避免用户更改模拟器的默认热键设置。
我已经成功地使用SendInput模拟了我想要的任何按键,除了“Print Screen”键。
我尝试过使用虚拟键代码,但没有成功,我认为这在全屏应用程序中无法工作。因此,该部分代码被注释掉了。(编辑:更好的解释-直接输入软件会忽略虚拟键代码)
使用扫描码,我可以按下任何键-几乎任何键。Print Screen似乎是个例外。
这是我用于扫描码的参考资料: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx 以下是最小可行代码,以重现问题。如果你运行它,按下一个键然后迅速切换到记事本并等待2秒钟,它应该在记事本中按下字母“q”,然后退出。

将扫描码从0x10(q)更改为0x37(Print Screen),确保在KEY DOWN和KEY UP两个位置都进行更改。

现在再次运行它,按下一个键并等待。要查看Print Screen是否起作用,请打开MS Paint或其他应用程序,然后按CTRL + V,看看是否获得了桌面的截图。但是它不起作用!但是,如果您手动按Print Screen,然后按CTRL + V进入MS Paint,则可以正常工作。

为什么Print Screen键无法正常工作?

#include "stdafx.h"

//For create process & keyboard codes
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

int main()
{
    INPUT ip = {};

    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0;
    ip.ki.wVk = 0;
    ip.ki.dwExtraInfo = 0;
    ip.ki.dwFlags = 0;

    printf("Press a key, then taskswitch.\n");
    system("pause");
    Sleep(2000);

    //KEY DOWN
    ip.ki.wScan = 0x10; //0x37 PrintScreen, 0x10 Q
    ip.ki.dwFlags = KEYEVENTF_SCANCODE;
    //ip.ki.wVk = VK_SNAPSHOT;
    //ip.ki.dwFlags = 0;
    SendInput(1, &ip, sizeof(INPUT));

    //KEY UP
    ip.ki.wScan = 0x10;
    ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; 
    //ip.ki.wVk = VK_SNAPSHOT;
    //ip.ki.dwFlags = KEYEVENTF_KEYUP; 
    SendInput(1, &ip, sizeof(INPUT));

    printf("Done.\n");
    system("pause");
    return 0;
}

4
也许不是问题的根源,但代码中包含了传统的漏洞,即在处理一系列输入事件时多次调用SendInput而非进行单次调用并传入数组。 - zett42
1
VK_SNAPSHOT is 0x2C, not 0x37 - Barmak Shemirani
@zett42 谢谢,尽管我相当确定这不会解决这个特定的问题,但我想以“正确”的方式来做,所以我会考虑重新编写它。虽然在当前实现中,如果需要,我可以在序列中添加延迟。 - Domarius
@zett42 尽管阅读了您的文章链接后,将其作为数组发送的目的是用户无法意外插入真实按键并搞乱顺序,这绝对是我想要的可靠性,因此我将采用数组方法,谢谢 :) - Domarius
@zett42 好的,我刚试了一下 - 猜猜怎么着 - 它出问题了!组合键(例如CTRL+S)有时会被模拟器软件忽略,并被解释为它们各自的按键!有时它能工作,有时它不能。当我回到我的方法,即在按键和释放之间延迟1/4秒时,它每次都能完美地工作。因此,我肯定不认为这是一个错误,而是一个设计选择。 - Domarius
显示剩余2条评论
2个回答

2

使用wVk代替wScan,并确保未设置KEYEVENTF_SCANCODE,因为它会忽略wVk。您必须使用VK_SNAPSHOT

INPUT ip[2] = { 0 };

ip[0].type = INPUT_KEYBOARD;
ip[0].ki.wVk = VK_SNAPSHOT;

ip[1] = ip[0];
ip[1].ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(2, ip, sizeof(INPUT));

这在DirectInput游戏中不起作用 :( 就像我说的,我在全屏运行的模拟器中使用虚拟键没有成功,所以我转而使用扫描码。 - Domarius
虽然我欣赏使用 SendInput 的关键序列数组的演示,但是我会仔细研究它。 - Domarius
显然我不太理解这个问题。当您在实际键盘上物理按下[Print Screen]键时,它是否可以正常工作? - Barmak Shemirani
你需要知道的一切都在原帖中 :) 正如我所说; 你可以按Print Screen键,然后将桌面截图粘贴到MS Paint中。 DirectInput忽略虚拟键代码(你使用的键代码风格),因此当模拟器(FS-UAE)运行时,使用虚拟键发送输入没有效果。你需要编写它以使用扫描码(我在我的帖子中所做的),但是Print Screen键的扫描码特别没有效果,并且在我下面为自己编写的答案中有一些新信息解释了为什么。 - Domarius

2

好的,我来回答自己的问题;

所以我做了更多的研究,找到了这篇关于扫描码细节的惊人帖子,看起来Print Screen是一个奇怪的按键,需要一个特殊的序列才能正确触发; https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names (另外,还有一篇关于三种不同扫描码集历史的文章,它可以让我们了解为什么某些按键可能很奇怪,因为新的按键被添加到标准中)http://www.quadibloc.com/comp/scan.htm

然而,我仍然无法通过尝试那篇文章中的各种代码序列来使其工作 - 最终,我学会了FS-UAE支持用于保存截图的备用按键序列:F12-S,我现在使用它,效果非常好。

所以我可能已经避免了这个问题,但是需要在扫描码级别上合法模拟Print Screen键按下的人将会遭受困扰,而不使用“虚拟键代码”...这是一项神秘而深奥的任务,我仍然没有完成。


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