在记事本/ wordpad中处理SendInput函数,就像CTRL修饰符已关闭一样

时间:2014-12-11 10:17:48

标签: c# wpf winapi sendinput on-screen-keyboard

背景

我正在编写一个32位WPF(C#)应用程序,它可用作屏幕键盘。它将选定的击键发布到聚焦窗口,就像按下了物理键一样,就像微软的屏幕键盘,OSK.exe的行为一样。

问题:

我正在使用InputSimulator库取得了一些成功(代码在这里:InputSimulator class which builds the INPUT array),但发现记事本未按预期识别某些击键,例如箭头键的行为就像CTRL被按下一样。同样,WIN键未按预期工作,如果Windows将输入视为Ctrl + Win,也可以解释。

擅自解决方案:

我将InputSimulator源移植到我的项目中,并根据对OSK.exe发送的SendInput的调用(使用API​​ Monitor捕获)对键盘发送到SendInput的方式进行了一些修改。我在KeyDown / KeyUp中观察到的(并在我的代码中复制)的主要差异是:

  • InputSimulator传递一个Keycode和一个ExtendedKey标志(如果该键被扩展),以及释放该键时的KeyUp标志。
  • OSK为大多数密钥添加了密钥的扫描码和ScanCode标志。
  • OSK也有一些其他差异;根本没有传递KeyCode的个别密钥,根本没有传递ScanCode,以及哪些密钥需要ExtendedKey标记的差异。

我更改代码以复制OSK调用SendInput的方式的结果是,现在更多的键表现得好像我的目标/焦点应用程序(通常是记事本或写字板)正在检测到CTRL。但是,通过直接比较我的应用程序和API监视器中的OSK,我相信我对SendInput的调用与OSK的调用相同。

N.B。 OSK在我的Windows 8.1(64位)笔记本电脑上完美运行。

隔离的问题空间:

为了最大限度地减少问题空间,我模拟了一个单键向下/向上键组合的' S'在我重新启动的PC上使用我的应用程序的密钥(这样我可以确保按键状态没有受到先前运行或物理键击的污染)。目标是记事本,然后写字板 - 两者都打开了“另存为”的反应。对话框,表明他们已将我的击键解释为CTRL + S. API Monitor仅检测到2次SendInput调用(KeyDown然后是KeyUp),这些调用使用OSK匹配相同的实验。这是日志;

2014-12-10 21:29:54,650 Calling native method SendInput with params:
nInputs:1
pInputs[0]:
    Type:1(Keyboard)
    Data:
        MOUSEINPUT:
                X:2031699
                Y:8
                MouseData:0
                Flags:0 ()
                Time:0
        KEYBDINPUT:
                KeyCode:83(VK_S)
                Scan:31
                Flags:8 (KEYEVENTF_SCANCODE)
                Time:0
                ExtraInfo:0
        HARDWAREINPUT:
                Msg:2031699
                ParamL:8
                ParamH:0

cbSize:28

2014-12-10 21:29:54,651 Calling native method SendInput with params:
nInputs:1
pInputs[0]:
    Type:1(Keyboard)
    Data:
        MOUSEINPUT:
                X:2031699
                Y:10
                MouseData:0
                Flags:0 ()
                Time:0
        KEYBDINPUT:
                KeyCode:83(VK_S)
                Scan:31
                Flags:10 (KEYEVENTF_KEYUP | KEYEVENTF_SCANCODE)
                Time:0
                ExtraInfo:0
        HARDWAREINPUT:
                Msg:2031699
                ParamL:10
                ParamH:0

cbSize:28

唯一明显的区别是OSK将cbSize参数传递为40,我无法伪造(如果手动传递40则调用失败)。我的体型是28,通过以下传递我得到的。我不知道为什么大小不同,因为我的结构定义与MSDN文档相匹配(我没有从原始的InputSimulator代码修改它们。)

var cbSize = Marshal.SizeOf(typeof (INPUT));

其他被诱惑的修正:

  1. 我已经尝试在指定ScanCode(和ScanCode标志)时将VirtualCode参数留空(0),但这不会改变结果。来自这里的想法:SO question

  2. 我尝试过向keybd_event添加尾随调用,但这不会改变结果。来自这里的想法:MSDN thread

    (keybd_event(0x41,0,0,0);)

  3. 我尝试在每次调用SendInput之间添加线程休眠,即KeyDown和KeyUp调用之间的延迟。

  4. 我修改了我的结构和winapi函数定义以匹配PINVOKE.net

  5. 我已经将我的应用程序重新编译为64位(在我的64位机器上) - 这将cbSize更正为40,但没有改变行为。

  6. 任何帮助都会非常感激。还有关于我可以使用的其他调试工具的建议吗?我试图调试OSK可能正在调用的所有键盘功能(例如,检测对keybd_event的其他调用),但除了调用SendInput之外,没有任何记录。

1 个答案:

答案 0 :(得分:1)

是的,问题是PEBCAK。

我错过了可以想象到的最明显的事情 - 我在测试时一直使用的触发信号(即表示我想按下当前聚焦的屏幕键的信号)是左CTRL键。我一直按下CTRL键,然后想知道为什么Windows认为按下了CTRL键。现在拍我。

我已经使用了很长时间,而且一直专注于可能行为不端的WinAPI电话的细节,我错过了最明显的原因。

我会留在这里提醒自己使用奥卡姆的剃刀。

上面的一些调试过程可能对某些人有用;我的InputSimulator代码的更改版本工作正常,原始的,未改变的代码也是如此。