发送输入函数在记事本/写字板中处理,就好像 CTRL 修饰符已关闭一样

本文关键字:CTRL 一样 记事本 函数 输入 处理 | 更新日期: 2023-09-27 18:33:59

>背景:

我正在编写一个 32 位 WPF (C#( 应用程序,它用作屏幕键盘。它将选定的击键发布到焦点窗口,就像按下物理键一样,与Microsoft屏幕键盘 OSK.exe的行为完全相同。

问题:

我使用 InputSimulator 库取得了一些成功(这里的代码:构建 INPUT 数组的 InputSimulator 类(,但发现记事本无法按预期识别某些击键,例如箭头键的行为就像按住 CTRL 一样。同样,WIN 键未按预期工作,如果 Windows 将输入视为 Ctrl+Win,也可以解释这一点。

尝试的解决方案:

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

  • InputSimulator 在释放密钥时传递一个键代码和一个扩展键标志(如果键已扩展(,以及 KeyUp 标志。
  • OSK 添加密钥的扫描码
  • 和大多数密钥的扫描码标志。
  • OSK 还有一些其他差异:根本不传递密钥代码的单个密钥,
  • 根本不传递扫描代码的单个密钥,以及哪些密钥需要扩展密钥标志的差异。

我更改代码以复制 OSK 调用 SendInput 的方式的结果是,现在更多的键的行为就像我的目标/重点应用程序(通常是记事本或写字板(检测到 CTRL 一样。但是,通过在 API Monitor 中直接比较我的应用程序和 OSK,我相信我对 SendInput 的调用与 OSK 的调用相同。

注意:OSK在我的Windows 8.1(64位(笔记本电脑上完美运行。

隔离的问题空间:

为了最大程度地减少问题空间,我在新重新启动的PC上模拟了应用程序中"S"键的单个键向下/向上键组合(这样我就可以确保按键按下状态没有受到先前运行或物理击键的污染(。目标是记事本,然后是写字板 - 两者都通过打开"另存为"对话框做出反应,这表明他们将我的击键解释为 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. 我尝试在指定扫描码(和扫描码标志(时将虚拟代码参数留空 (0(,但这不会改变结果。从这里的想法:所以问题

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

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

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

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

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

任何帮助将不胜感激。还有关于我可以使用的其他调试工具的任何建议?我尝试调试 OSK 可能正在调用的所有键盘函数(例如,检测对 keybd_event 的其他调用(,但除了对 SendInput 的调用之外,没有记录任何函数。

发送输入函数在记事本/写字板中处理,就好像 CTRL 修饰符已关闭一样

对,所以问题是PEBCAK。

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

我已经使用它很长时间了,并且一直专注于可能行为异常的 WinAPI 调用的细节,以至于我错过了最明显的原因。

我将把这个留在这里,以提醒自己使用奥卡姆剃刀。

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