0

A WPF application has a Test() method that is called when a keyboard shortcut is pressed CTRL+G.

The method call works because the string test is printed to the console, from the first line of the method.


The method should programmatically press the key combination CTRL+A to select the text in any input field, but this does not happen.

I tried 3 ways:


First: The System.Windows.Forms.SendKeys.SendWait() method, which takes a string, where ^ is CTRL - according to documentation

private void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    SendKeys.SendWait("^A");
}

However, there is no pressing.


Second: Implementation via user32.dll, solution taken from here:

[DllImport("user32.dll", SetLastError = true)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
public static void PressKey(Keys key, bool up)
{
    const int KEYEVENTF_EXTENDEDKEY = 0x1;
    const int KEYEVENTF_KEYUP = 0x2;
    if (up)
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
    else
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
}

private void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    PressKey(Keys.ControlKey, up: false);
    PressKey(Keys.A, up: false);
    PressKey(Keys.A, up: true);
    PressKey(Keys.ControlKey, up: true);
}

But in this case, nothing happens.


Third: Installed the package: Install-Package InputSimulator:

private static void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    var simu = new InputSimulator();
    simu.Keyboard.ModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.VK_A);
}

Full code: https://pastebin.com/ay8vRtjA

There are no errors, what am I doing wrong?

denisnumb
  • 83
  • 13
  • IMO you should have some other view model/controller class that would have method to handle such key press. Then I would write unit test for that controller class to properly test the logic. – Michał Turczyn Apr 24 '22 at 10:16
  • @MichałTurczyn I've updated the question with a link to the full code if that can help somehow – denisnumb Apr 24 '22 at 10:36
  • When you say "in any input field", do you mean any input field in _your_ application or in _any_ application? – Visual Vincent Apr 24 '22 at 10:39
  • is the text field your trying to select all text in focus? – Ben Steele Apr 24 '22 at 10:40
  • @VisualVincent I mean input field in **in any application** – denisnumb Apr 24 '22 at 10:48
  • @BenSteele yes, field in focus. I also try to press other keyboard buttons like `win`-key, but that also not work – denisnumb Apr 24 '22 at 10:50
  • I just tested this. The issue is because you're still holding down `ALT` when the code executes, making the resulting combination `CTRL + ALT + A`. – Visual Vincent Apr 24 '22 at 10:53
  • @VisualVincent indeed - it is. I would never have thought of it myself, now I understand why it was necessary to make tests with the output of all the keys pressed. And what is the right way to avoid this problem? Set a delay when executing a method? Or is there some other way? – denisnumb Apr 24 '22 at 10:56

1 Answers1

1

The key combination technically works, but the code executes before you have any time to release the ALT key, making the final combination CTRL + ALT + A instead of CTRL + A. I may be overlooking some simpler solution, but the way I found (mostly) works is:

  • Intercept the ALT key
    • If G is pressed while ALT is down, execute the command
    • If G wasn't pressed, allow the keystroke through and send a simulated ALT (so that hotkeys in other applications can still be activated)

It's a hacky workaround and still messes up some regular functionality (for instance if you press a hotkey like ALT + A to open a menu, the menu will close as soon as you release ALT), but it makes your hotkey work.

I used a library that I created a couple of years back, called InputHelper, to set up a global keyboard hook to intercept the keystrokes and execute the hotkey. I've yet to publish this to NuGet, so for now you'll have to download it via Releases and add the DLL as a reference in your project.

You'll also need to add a reference to System.Windows.Forms.

using System;
using System.Windows;
using System.Windows.Forms;

namespace HotkeyTest
{
    public partial class MainWindow : Window
    {
        InputHelper.Hooks.KeyboardHook kbHook = new InputHelper.Hooks.KeyboardHook();

        bool AltHotkeyConsumed = false;

        public MainWindow()
        {
            InitializeComponent();

            kbHook.KeyDown += KeyboardHook_KeyDown;
            kbHook.KeyUp += KeyboardHook_KeyUp;
        }

        private void KeyboardHook_KeyUp(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
        {
            if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
            {
                if(!AltHotkeyConsumed)
                    // If no hotkey was consumed, press the key again (otherwise it will just be blocked altogether)
                    InputHelper.Keyboard.PressKey(e.KeyCode);
                else
                    AltHotkeyConsumed = false;
            }
        }

        private void KeyboardHook_KeyDown(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
        {
            if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
                e.Block = true;

            if(e.Modifiers == InputHelper.ModifierKeys.Alt)
            {
                if(e.KeyCode == Keys.G)
                {
                    Test();
                    AltHotkeyConsumed = true;
                    e.Block = true;
                }
                else
                {
                    e.Block = false;
                }
            }
        }

        private static void Test()
        {
            InputHelper.Keyboard.SetKeyState(Keys.LControlKey, true);
            InputHelper.Keyboard.PressKey(Keys.A);
            InputHelper.Keyboard.SetKeyState(Keys.LControlKey, false);
        }
    }
}
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • If you replace `alt` with `control`, then when you try to press `CTRL+A` yourself, the text will not be selected, but will simply add the character "a" to the text. How to avoid it? – denisnumb Apr 24 '22 at 13:44
  • @denisnumb When the modifier key(s) (in this case `CTRL`) are the same as the hotkeys you don't need all the extra code. A simple `KeyDown` hook is all you require: https://pastebin.com/CBNcpbEy – Visual Vincent Apr 24 '22 at 16:36
  • My initial solution was a hacky one, and mainly focused at resolving the problem at hand. Remapping modifiers only for certain hotkeys is not a simple task as it involves tricking the OS that you're in a different keyboard state. If you do have a case where you need to remap `CTRL` to another modifier, off the top of my head you would likely have to track which keys were pressed after `CTRL`, and once you've determined that they're not a hotkey, replay them again in the correct order without having the keyboard hook block them. – Visual Vincent Apr 24 '22 at 16:52