C#全局键盘钩子,用于从控制台应用程序打开表单

时间:2017-09-02 11:25:42

标签: c# forms hotkeys global-hotkey

所以我有一个带有Form的C#控制台应用程序,我想用热键打开它。比方说,例如 Ctrl + < 打开表单。所以我现在得到了处理globalkeylistener的代码,但看起来我实现它失败了。它做了一个while循环来阻止它关闭程序,我试图用kbh_OnKeyPressed方法从用户那里获得一个输入。

我试着用这种方式实现它:

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Globalkey
{
    static class Program
    {
        [DllImport("user32.dll")]
        private static extern bool RegisterHotkey(int id, uint fsModifiers, uint vk);

        private static bool lctrlKeyPressed;
        private static bool f1KeyPressed;


        [STAThread]
        static void Main()
        {

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
            kbh.OnKeyPressed += kbh_OnKeyPressed;
            kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
            kbh.HookKeyboard();

            while(true) { }

        }

        private static void kbh_OnKeyUnpressed(object sender, Keys e)
        {
            if (e == Keys.LControlKey)
            {
                lctrlKeyPressed = false;
                Console.WriteLine("CTRL unpressed");
            }
            else if (e == Keys.F1)
            {
                f1KeyPressed = false;
                Console.WriteLine("F1 unpressed");
            }
        }

        private static void kbh_OnKeyPressed(object sender, Keys e)
        {
            if (e == Keys.LControlKey)
            {
                lctrlKeyPressed = true;
                Console.WriteLine("CTRL pressed");
            }
            else if (e == Keys.F1)
            {
                f1KeyPressed = true;
                Console.WriteLine("F1 pressed");
            }
            CheckKeyCombo();
        }

        static void CheckKeyCombo()
        {
            if (lctrlKeyPressed && f1KeyPressed)
            {
                Application.Run(new Form1());
            }
        }
    }
}

1 个答案:

答案 0 :(得分:7)

您需要的是低级键盘挂钩。

这看起来有点像这样:

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

public class LowLevelKeyboardHook
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_SYSKEYDOWN = 0x0104;
    private const int WM_KEYUP = 0x101;
    private const int WM_SYSKEYUP = 0x105;

    [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);

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

    public event EventHandler<Keys> OnKeyPressed;
    public event EventHandler<Keys> OnKeyUnpressed;

    private LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    public LowLevelKeyboardHook()
    {
        _proc = HookCallback;
    }

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

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

    private 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 IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyPressed.Invoke(this, ((Keys)vkCode));
        }
        else if(nCode >= 0 && wParam == (IntPtr)WM_KEYUP ||wParam == (IntPtr)WM_SYSKEYUP)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyUnpressed.Invoke(this, ((Keys)vkCode));
        }

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

要实现它,您可以使用以下内容:

kbh = new LowLevelKeyboardHook();
kbh.OnKeyPressed += kbh_OnKeyPressed;
kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
kbh.HookKeyboard();

事件可以这样处理:

bool lctrlKeyPressed;
bool f1KeyPressed;

void kbh_OnKeyPressed(object sender, Keys e)
{
    if (e == Keys.LControlKey)
    {
        lctrlKeyPressed = true;
    }
    else if (e == Keys.F1)
    {
        f1KeyPressed= true;
    }
    CheckKeyCombo();
}

void kbh_OnKeyUnPressed(object sender, Keys e)
{
    if (e == Keys.LControlKey)
    {
        lctrlKeyPressed = false;
    }
    else if (e == Keys.F1)
    {
        f1KeyPressed= false;
    }
}

void CheckKeyCombo()
{
    if (lctrlKeyPressed && f1KeyPressed)
    {
        //Open Form
    }
}

为了实际理解,我建议你阅读P / Invoke。这是利用Windows提供的非托管API。

有关P / Invoke possibilites的完整列表,pinvoke.net是一个很好的来源。

为了更好地理解,The official MSDN Website也是一个很好的来源。

修改

您似乎实际上使用的是控制台应用程序,而不是WinForm。在这种情况下,您必须以不同的方式运行程序:

[STAThread]
static void Main()
{

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
    kbh.OnKeyPressed += kbh_OnKeyPressed;
    kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
    kbh.HookKeyboard();

    Application.Run();

    kbh.UnHookKeyboard();

}

Application Class的Run()方法为您的应用程序启动标准循环。这对于Hook工作是必要的,因为据我所知,没有这个循环的控制台应用程序无法触发那些全局关键事件。

使用此实现,按下并释放已定义的键将提供以下输出:

Console Output

注意:我显然已经取代了

Application.Run(new Form1());

使用

CheckKeyCombo()方法中

Console.WriteLine("KeyCombo pressed");