Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture keyboard shortcuts and forward

Tags:

c#

What I need to do :

I need to capture all shortcut key presses such as Ctrl+S from a specific application. Any key combo, even if it's not a shortcut for that application.

Then my midway application that capture these keys need to validate those key combination and check if another of our apps that are running can respond to that key, and if it can send the command to it.

What I have so far :

Since we wrote the other apps we can easily send the keys to be processed that's not a problem. I can get the window handle of the application. I need to capture the shortcut keys. This app I know it's build in C++. I am looking to find a way to capture an equivalent of the following event in Winforms :

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

Right now everything is working as expected on the forward key part. I am only missing the capture of keys on that handle. I really don't want to have to adventure myself in hooking the whole keyboard and detect whether the key stroke is done in my apps or not and do I need to cancel the key or continue the process. I would prefer also getting key combination events only. I would prefer not receiving all letter the guy press when he type in a textbox or anything. I'm really looking for anything starting with CTRL, ALT, SHIFT or any combination of them

Example of what I want to do :

uncontrolled application : Notepad.exe my midway app : ShortcutHandler.exe my target applications : A.exe, B.exe

ShortcutHandler.exe will listen to Notepad.Exe shortcuts then forward them to A.exe and B.exe

Situation :

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
like image 958
Franck Avatar asked Aug 28 '15 14:08

Franck


1 Answers

Some time ago I have to do something like you, so I found this article: A Simple C# Keyboard Hook, and with this I was able to do what I need it.

But this is a complex code and as you said so, you don't want do get all key down. For my program I created a KeyboardHook class that make easy to work with the code obtained of previous article.

So there is a snippet code of what you can do with the KeyboardHook class:

// 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: If you put this on your ShortcutHandler app the app will still get the keys.

And below is the KeyboardHook code:

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);
like image 93
Jonny Piazzi Avatar answered Oct 08 '22 17:10

Jonny Piazzi