Search code examples
c#wpfwinapisendinputon-screen-keyboard

SendInput function handled in notepad/wordpad as if CTRL modifier is down


BACKGROUND:

I am writing a 32 bit WPF (C#) application which functions as an on-screen keyboard. It publishes the selected key strokes to the focused window as if physical keys had been pressed, exactly as the Microsoft On-Screen Keyboard, OSK.exe behaves.

PROBLEM:

I was using the InputSimulator library with some success (code here: InputSimulator class which builds the INPUT array), but found that certain key strokes were not being recognised by Notepad as expected, e.g. the arrow keys were behaving as if CTRL was being held down. Similarly, the WIN key was not working as expected, which could also be explained if Windows was treating the input as Ctrl+Win.

ATTEMPTED SOLUTION:

I ported the InputSimulator source into my project and made some modifications to how keystrokes are sent to SendInput, based on the calls to SendInput that OSK.exe sends (captured using API Monitor). The key differences I observed (and replicated in my code) for a KeyDown/KeyUp are:

  • InputSimulator passes a Keycode, and an ExtendedKey flag (if the key is extended), plus the KeyUp flag when releasing the key.
  • OSK adds the key's Scancode, and the ScanCode flag for the majority of keys.
  • OSK also has some other differences; individual keys where the KeyCode is not passed at all, where the ScanCode is not passed at all, and differences in which keys require the ExtendedKey flag.

The result of my changing the code to replicate how OSK calls SendInput was that even more keys now behave as if CTRL is being detected by my target/focussed application (typically Notepad or Wordpad). However, from direct comparison of my app and OSK in API Monitor I believe that my calls to SendInput are identical to OSK's calls.

N.B. OSK works flawlessly on my Windows 8.1 (64 bit) laptop.

ISOLATED PROBLEM SPACE:

To minimise the problem space I simulated a single key down/key up combination of the 'S' key from my application on a newly restarted PC (so that I can be sure the key down states were not contaminated from previous runs or physical key strokes). The target was Notepad and then WordPad - both reacted by opening the 'Save As' dialog, suggesting that they had interpreted my keystrokes as CTRL+S. API Monitor detected only 2 calls to SendInput (the KeyDown and then KeyUp), and these calls matched the same experiment using OSK. Here is the log;

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

The only noticeable difference is that OSK passes the cbSize param as 40, which I cannot fake (the call fails if I manually pass 40). My size is 28, which I get by passing the below. I have no idea why the sizes differ as my structure definitions match the MSDN documentation (I have not modified these from the original InputSimulator code).

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

OTHER ATTEMPTED FIXES:

  1. I have tried leaving the VirtualCode param blank (0) when a ScanCode (and ScanCode flag) are specified, but this does not change the result. Idea from here: SO question

  2. I have tried adding a trailing call to keybd_event, but this does not change the result. Idea from here: MSDN thread

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

  3. I have tried adding a thread sleep between each call to SendInput, i.e. a delay between the KeyDown and KeyUp calls.

  4. I have modified my structs and winapi function definitions to match PINVOKE.net

  5. I have recompiled my application as 64 bit (on my 64 bit machine) - this corrected the cbSize to 40, but did not change the behaviour.

Any help would be GREATLY appreciated. Also any suggestions regarding other debugging tools I could use? I have attempted to debug all Keyboard functions which OSK may be calling (to detect other calls to keybd_event, for example), but none are logged besides the calls to SendInput.


Solution

  • Right, so the issue was PEBCAK.

    I missed the most obvious thing imaginable - the trigger signal that I have been using while testing (i.e. the signal to say that I want to press the currently focused an on-screen key) is the left CTRL key. I've been pressing the CTRL key and then wondering why Windows thinks the CTRL key is pressed. Shoot me now.

    I've been using it for so long, and have been so focused on the details of the potentially misbehaving WinAPI calls that I missed the most obvious cause.

    I will leave this here to remind myself to use Occam's Razor.

    Some of the debugging journey above may be useful to someone; my altered version of the InputSimulator code works fine, as does the original, unaltered code.