在不聚焦窗口的情况下捕获键

时间:2013-03-15 09:18:41

标签: c# keyboard-hook global-hotkey

我有一个应用程序总是检查是否按下了像 F12 这样的键。它不需要关注我的应用程序的主窗口。我试过这段代码:

public int a = 1;
    // DLL libraries used to manage hotkeys
    [DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
    [DllImport("user32.dll")]
    public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    const int MYACTION_HOTKEY_ID = 1;

    public Form1()
    {
        InitializeComponent();
        // Modifier keys codes: Alt = 1, Ctrl = 2, Shift = 4, Win = 8
        // Compute the addition of each combination of the keys you want to be pressed
        // ALT+CTRL = 1 + 2 = 3 , CTRL+SHIFT = 2 + 4 = 6...
        RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F12);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID)
        {

            a++;
            MessageBox.Show(a.ToString());
        }
        base.WndProc(ref m);
    }

我把0放到这一行RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F12);,这样只有当按下 F12 时才会捕获它。

但它没有用。我该如何解决这个问题?

在这里,我无法理解一些行:

const int MYACTION_HOTKEY_ID = 1;
m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID
base.WndProc(ref m);

任何人都可以帮我理解这些内容吗?

4 个答案:

答案 0 :(得分:2)

  

但它没有用。我该如何解决这个问题?

你是什么意思"它没有工作"?您问题中的代码对我来说是正确的。

它可能无法正常工作的唯一原因是因为RegisterHotKey函数返回错误并且您没有检查它。要使其工作,您需要将SetLastError attribute添加到其声明中,这会导致运行时缓存它设置的Win32错误代码。完成此操作后,您可以通过调用GetLastWin32Error function来检查错误代码(如果函数返回false)。我建议使用此函数的结果生成并抛出Win32Exception

修改RegisterHotKey的声明,如下所示:

[DllImport("user32.dll", PreserveSig = false)]
public static extern bool RegisterHotKey(IntPtr hWnd,
                                         int id,
                                         uint fsModifiers,
                                         Keys key);

您对该功能的调用如下:

if (!RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, Keys.F12))
{
   throw new Win32Exception(Marshal.GetLastWin32Error());
}

一旦完成,我怀疑您会看到异常被抛出错误消息:

  

热键已注册

嗯,这使调试问题变得更加简单,现在还没有!您可能需要选择不同的热键,因为RegisterHotKey函数文档明确告诉我们:

  

F12 键保留供调试器一直使用,因此不应将其注册为热键。即使您没有调试应用程序,也会保留 F12 ,以防内核模式调试器或实时调试器驻留。

当我运行代码并注册 F11 作为热键时,它对我来说很好。


  

在这里,我无法理解如下行:

const int MYACTION_HOTKEY_ID = 1;
m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID
base.WndProc(ref m);
     

任何人都可以帮我理解这些内容吗?

不确定

  1. 第一行声明一个常量值,该值唯一标识使用RegisterHotKey函数安装的热键。更具体地说,它对应于函数的id参数。你在初次通话中传了它。

  2. 这将检查窗口过程(WndProc),以查看正在处理的消息(Msg)是否为WM_HOTKEY message。只要按下您在WM_HOTKEY功能中注册的热键,RegisterHotKey消息就会自动发布到您的窗口。

    你不应该直接使用幻数0x0312,因为你并不是唯一一个不确定它意味着什么的人。相反,定义一个常量并改为使用它:

    const int WM_HOTKEY = 0x0312;
    m.Msg == WM_HOTKEY
    

    该条件测试的第二部分(&&之后的部分)检查消息的wParam字段,以查看按下的热键是否是您注册的热键。请记住,MYACTION_HOTKEY_ID是热键的唯一ID。 WM_HOTKEY消息文档告诉我们检查wParam是我们如何确定按下了哪个热键。

  3. 这会调用基类的窗口过程。换句话说,您已完成的操作会覆盖虚拟WndProc方法,允许您添加一些其他代码(处理WM_HOTKEY)。当您完成其他逻辑操作后,您希望继续使用基类的逻辑,以便转发该消息。

答案 1 :(得分:1)

为了做类似的事情,我使用SetWindowsHookEx实现了一个低级键盘钩子。这将捕获通过Windows的所有键盘消息,并允许您检查它们,并在必要时阻止它们继续前进。

在我的my KeyboardHandling project中查看RocketLauncher GitHub hobby project。您可以直接从中获取所需内容。我很快就会把它变成一个nuget包。

答案 2 :(得分:1)

您的代码没有错。但它在这里不起作用,因为保留了 F12 键,您可以尝试使用其他键,例如 F10 F11 等。

答案 3 :(得分:0)

我不知道为什么,但我觉得这与this问题有关......所以我会再次尝试解释它:

const int MYACTION_HOTKEY_ID = 1;

是保存用于标识热键的整数的位置。如果您需要注册多个热键,则必须声明标识其他热键的其他整数字段:

const int ANOTHER_ACTION_HOTKEY_ID = 2;
const int AND_ANOTHER_ACTION_HOTKEY_ID = 3;

然后,

m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID

是使您能够知道用户键入了哪个热键的条件。

0x0312(在您可以找到的here文档中也声明为WM_HOTKEY)是要知道是否已按下注册的热键:< / p>

  

按下某个键时,系统会查找所有热键的匹配项。在找到匹配时,系统将WM_HOTKEY消息发布到与热键相关联的窗口的消息队列。如果热键未与窗口关联,则WM_HOTKEY消息将发布到与热键关联的线程。


根据documentation,你不能使用 F12 热键:

  

F12密钥保留供调试器一直使用,因此不应将其注册为热键。即使您没有调试应用程序,也会保留F12,以防内核模式调试器或实时调试器驻留。