全球键盘捕获在C#应用程序

问题描述:

我想在应用程序中捕获键盘快捷方式,如果用户在应用程序外部按下键盘组合,则会触发一个对话框。类似于Google桌面搜索的Ctrl键,Ctrl键可以弹出搜索对话框。

I want to capture a keyboard shortcut in my application and trigger a dialog to appear if the user presses a keyboard combo even outside of the app. Similar to Google Desktop Search's Ctrl, Ctrl to bring up the search dialog.

我已经尝试使用一些键盘钩子模块,基本上使用Win32 interop来获得这个效果,每个实现我已经尝试绑定键盘在某种程度上,当你开始变得奇怪的行为,当应用程序做一些密集的事情。如加载大量的数据,这将导致键盘和鼠标锁定。

I have tried using some keyboard hook modules out there that basically use Win32 interop to get this effect but each implementation I've tried ties down the keyboard to some extent to where you start getting weird behaviors when the application is doing something intensive. Such as loading a large amount of data, this would cause the keyboard and mouse to lockup.

我正在寻找一个轻量级的解决方案,可以在没有绑定键盘和鼠标。

I'm looking for a lightweight solution that would allow this to be done without tying down the keyboard and mouse.

Stephen Toub 在C#中实现全局键盘钩子写了一篇伟大的文章

Stephen Toub wrote a great article on implementing global keyboard hooks in C#:

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

class InterceptKeys
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
    }

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

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

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Console.WriteLine((Keys)vkCode);
        }

        return CallNextHookEx(_hookID, nCode, wParam, 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);
}