C# WPF:如何以编程方式按下键盘按钮?

C# WPF: How to programmatically press a keyboard button?

WPF 应用程序有一个 Test() 方法,当按下键盘快捷键 CTRL+G.

方法调用有效,因为字符串 test 从方法的第一行打印到控制台。


该方法应以编程方式按组合键 CTRL+A 到 select 任何输入字段中的文本,但是这不会发生。

我试了3种方法:


首先:System.Windows.Forms.SendKeys.SendWait()方法,它取一个字符串,其中^CTRL——根据至 documentation

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

但是,没有按。


其次:通过user32.dll实现,解决方案取自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);
}

但在这种情况下,没有任何反应。


第三个: 安装包: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);
}

完整代码:https://pastebin.com/ay8vRtjA

没有错误,我做错了什么?

组合键在技术上是可行的,但代码在您有时间释放 ALT 键之前执行,形成最终组合 CTRL + ALT + A 而不是 CTRL + A。我可能忽略了一些更简单的解决方案,但我发现(大部分)有效的方式是:

  • 拦截ALT
    • 如果在按下ALT时按下G,则执行命令
    • 如果未按下 G,允许击键通过并发送模拟的 ALT(这样其他应用程序中的热键仍然可以被激活)

这是一个 hacky 解决方法,仍然会破坏一些常规功能(例如,如果您按 ALT + A 等热键打开菜单,菜单将立即关闭你释放 ALT),但它使你的热键起作用。

我使用了我几年前创建的库,称为 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 并将 DLL 作为参考添加到您的项目中。

您还需要添加对 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);
        }
    }
}