【问题标题】:Capture keyboard shortcuts and forward捕获键盘快捷键并转发
【发布时间】:2015-09-10 21:52:37
【问题描述】:

我需要做什么:

我需要从特定应用程序中捕获所有快捷键,例如 Ctrl+S。任何组合键,即使它不是该应用程序的快捷方式。

然后我捕获这些键的中间应用程序需要验证这些组合键并检查我们正在运行的另一个应用程序是否可以响应该键,以及它是否可以向它发送命令。

我目前所拥有的:

由于我们编写了其他应用程序,因此我们可以轻松地发送要处理的密钥,这不是问题。我可以获得应用程序的窗口句柄。我需要捕获快捷键。这个应用程序我知道它是用 C++ 构建的。我正在寻找一种在 Winforms 中捕获与以下事件等效的方法:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)

现在,前向关键部分的一切都按预期工作。我只是想念那个把手上的钥匙。我真的不想冒险连接整个键盘并检测按键是否在我的应用程序中完成,我是否需要取消按键或继续该过程。我也希望只获得组合键事件。我宁愿在他输入文本框或任何东西时不收到他按的所有信。我真的在寻找以 CTRLALTSHIFT 或它们的任意组合开头的任何东西

我想做的例子:

不受控制的应用程序:Notepad.exe 我的中途应用程序:ShortcutHandler.exe 我的目标应用程序:A.exe、B.exe

ShortcutHandler.exe 会监听 Notepad.Exe 快捷方式,然后将它们转发给 A.exe 和 B.exe

情况:

1 - in Notepad.exe press CTRL+H for replace
2 - ShortcutHandler.exe detect CTRL+H pressed on Notepad.exe
3 - ShortcutHandler.exe Analyse CTRL+H and knows it need to do some task
4 - ShortcutHandler.exe call Save on A.exe in reaction to CTRL+H in Notepad.exe
5 - ShortcutHandler.exe call Print report in B.exe in reaction to CTRL+H in Notepad.exe

【问题讨论】:

  • 如果您编写了其他应用程序,为什么不让您的应用程序告诉其他应用程序他们需要做任何事情?
  • @RowlandShaw 因为他想在 any 应用程序中挂钩任意按键/组合,然后在发生这种情况时在他的应用程序中执行一些操作。
  • 全局热键对您有帮助吗?你正在尝试做的事情听起来有点可疑 - 也许添加一些关于你想要达到的目标的解释会有所帮助
  • @xan 为什么他们会说“我真的不想冒险自己去挂接整个键盘并检测是否在我的应用程序中完成了击键”?
  • 全局热键的问题是它会触发很多,因为它发生在任何软件和任何控件中。我不想在每个应用程序上触发事件。然后我需要一种方法来确定是否按下了 ALT-G,如果它在 Outlook.exe 或 Excel.exe 中触发。

标签: c#


【解决方案1】:

前段时间我必须做一些像你一样的事情,所以我找到了这篇文章:A Simple C# Keyboard Hook,有了它我就可以做我需要的事情了。

但这是一个复杂的代码,正如你所说,你不想把所有的键都写下来。对于我的程序,我创建了一个KeyboardHook 类,可以轻松使用从上一篇文章中获得的代码。

因此,您可以使用KeyboardHook 类执行以下操作的 sn-p 代码:

// Put this on the begin of your form (like the constructor on FormLoad).
var hook = new KeyboardHook();

hook.KeyDown += (sender, e) => 
{
    // e.Control is a bool property if true Control is press.
    // e.Shift is a bool property if true Shift is press.
    // e.Key has a key that was press.

    // This if ignores anything that don't begin with Control or Shift.
    if(!e.Control && !e.Shift) return;

    // your code below:

    if(e.Control && e.Key == Keys.H)
    {
        // do your code here.
        // like: Analyse CTRL+H and knows it need to do some task.
    }
};

hook.Start(); // Until here goes in the begin of your form.


// Put this on the end of your form (like in the Dispose or FormClose).
hook.Release();
hook.Dispose();

PS:如果你把它放在你的 ShortcutHandler 应用程序上,应用程序仍然会获取密钥。

下面是KeyboardHook代码:

using System.Runtime.InteropServices;
using System.Windows.Forms;

public class KeyboardHook : IDisposable
{
    #region Fields

    private bool _lControlKeyIsDown;
    private bool _rControlKeyIsDown;
    private bool _lShiftKeyIsDown;
    private bool _rShiftKeyIsDown;

    #endregion

    #region Properties

    private bool ControlIsDown
    {
       get { return _lControlKeyIsDown || _rControlKeyIsDown; }
    }

    private bool ShiftIsDown
    {
        get { return _lShiftKeyIsDown || _rShiftKeyIsDown; }
    }

    #endregion

    #region Constructors

    public KeyboardHook()
    {
        _proc = HookCallback;
    }

    #endregion

    #region Events

    public event HookKeyDownHandler KeyDown;

    #endregion

    #region Methods

    public void Start()
    {
        _hookID = SetHook(_proc);
    }

    private static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (var curProcess = Process.GetCurrentProcess())
        using (var curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            var vkCode = Marshal.ReadInt32(lParam);
            var key = (Keys)vkCode;

            if (wParam == (IntPtr)WM_KEYDOWN)
            {
                switch (key)
                {
                    case Keys.LControlKey:
                        _lControlKeyIsDown = true;
                        break;
                    case Keys.RControlKey:
                        _rControlKeyIsDown = true;
                        break;
                    case Keys.LShiftKey:
                        _lShiftKeyIsDown = true;
                        break;
                    case Keys.RShiftKey:
                        _rShiftKeyIsDown = true;
                        break;
                    default:
                        if (KeyDown != null)
                        {
                            var args = new HookKeyDownEventArgs((Keys)vkCode, ShiftIsDown, ControlIsDown);
                            KeyDown(this, args);
                        }
                        break;
                }
            }
            if (wParam == (IntPtr)WM_KEYUP)
            {
                switch (key)
                {
                    case Keys.LControlKey:
                        _lControlKeyIsDown = false;
                        break;
                    case Keys.RControlKey:
                        _rControlKeyIsDown = false;
                        break;
                    case Keys.LShiftKey:
                        _lShiftKeyIsDown = false;
                        break;
                    case Keys.RShiftKey:
                        _rShiftKeyIsDown = false;
                        break;
                }
            }
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    public void Release()
    {
        UnhookWindowsHookEx(_hookID);
    }

    public void Dispose()
    {
        Release();
    }

    #endregion

    #region Interoperability

    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private readonly LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    #endregion
}

public class HookKeyDownEventArgs : EventArgs
{
    #region Fields

    private readonly Keys _key;
    private readonly bool _shift;
    private readonly bool _control;

    #endregion

    #region Properties

    public Keys Key
    {
        get { return _key; }
    }

    public bool Shift
    {
        get { return _shift; }
    }

    public bool Control
    {
        get { return _control; }
    }

    #endregion

    #region Constructors

    public HookKeyDownEventArgs(Keys key, bool shift, bool control)
    {
        _key = key;
        _shift = shift;
        _control = control;
    }

    #endregion
}

public delegate void HookKeyDownHandler(object sender, HookKeyDownEventArgs e);

【讨论】:

  • 这非常有效。但是您要补充一点,您需要在示例调用中调用Start() 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
相关资源
最近更新 更多