Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting Ctrl+V with RegisterHotKey but not intercepting it

I need to detect when a user presses Ctrl+V(regardless of window focus - my app will likely be minimised) but I must not stop the actual paste operation.

I have tried a few things: (I am successfully binding to keystrokes with RegisterHotKey)

I have:

protected override void WndProc(ref Message m)
{
  if (m.Msg == 0x312)
    hotKey();
  base.WndProc(ref m);
}

and I've tried the following:

void hotKey()
{
  SendKeys.SendWait("^v"); //just puts 'v' instead of clipboard contents
}

and

void hotKey()
{
  SendKeys.SendWait(ClipBoard.GetText());
  /* This works, but since Ctrl is still down, it triggers
   * all the shortcut keys for the app, e.g. if the keyboard
   * contains 's' then instead of putting 's' in the app, it
   * calls Ctrl+S which makes the app think the user wants
   * to save.
   */
}

Currently the only working solution I have is to bind to something different, e.g. Ctrl+B and then call SendKeys.SendWait("^v"); however this isn't ideal.

An ideal solution would be if my window didn't intercept the keystroke in the first place, just reacted.

like image 212
Ozzah Avatar asked Jul 27 '11 01:07

Ozzah


1 Answers

You can do this by leveraging hooks using SetWindowsHookEx().

HHOOK WINAPI SetWindowsHookEx(
  __in  int idHook,
  __in  HOOKPROC lpfn,
  __in  HINSTANCE hMod,
  __in  DWORD dwThreadId
);

Basically, you can set up a low-level keyboard hook:

_hookHandle = SetWindowsHookEx(
    WH_KEYBOARD_LL,
    KbHookProc,                   // Your keyboard handler
    (IntPtr)0,
    0);                           // Set up system-wide hook.

to capture system-wide keyboard events. But it also allows you to make those keyboard events pass through to other apps. For your particular case, you can define KbHookProc as:

private static int KbHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0) // This means we can intercept the event.
    {
        var hookStruct = (KbLLHookStruct)Marshal.PtrToStructure(
                lParam,
                typeof(KbLLHookStruct));

        // Quick check if Ctrl key is down. 
        // See GetKeyState() doco for more info about the flags.
        bool ctrlDown = 
                GetKeyState(VK_LCONTROL) != 0 ||
                GetKeyState(VK_RCONTROL) != 0;

        if (ctrlDown && hookStruct.vkCode == 0x56) // Ctrl+V
        {
            // Replace this with your custom action.
            Clipboard.SetText("Hi");
        }
    }

    // Pass to other keyboard handlers. Makes the Ctrl+V pass through.
    return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
} 

I coded a quick and dirty WinForms app to illustrate this. For the full code listing, see http://pastebin.com/uCSvqwb4.

like image 111
Ilian Avatar answered Oct 19 '22 23:10

Ilian