在没有聚焦窗口的情况下捕获钥匙

本文关键字:情况下 钥匙 窗口 聚焦 | 更新日期: 2023-09-27 18:04:06

我有一个应用程序总是检查是否按下了像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);
有谁能帮我理解这些句子吗?

在没有聚焦窗口的情况下捕获钥匙

但它没有工作。我怎么解决这个问题?

你说"it didn't work"是什么意思?你问题中的代码在我看来是正确的。

它可能不工作的唯一原因是RegisterHotKey函数返回一个错误,而您没有检查它。要做到这一点,您需要将SetLastError属性添加到它的声明中,这将导致运行时缓存它设置的Win32错误代码。完成此操作后,可以通过调用GetLastWin32Error函数来检查错误代码(如果函数返回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消息。只要按下RegisterHotKey功能注册的热键,WM_HOTKEY消息就会自动发布到您的窗口。

    你不应该直接使用神奇的数字0x0312,因为你不是唯一一个不确定它的含义的人。相反,定义一个常量并使用它:

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

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

  3. 调用基类的窗口过程。换句话说,您所做的是覆盖虚拟WndProc方法,允许您添加一些额外的代码(您对WM_HOTKEY的处理)。当您完成了附加逻辑后,您希望继续处理基类的逻辑,因此您将消息转发到。

您的代码没有错误。但是它在这里不起作用,因为F12键是保留的,您可以尝试使用另一个键,如F10F11等。

我不知道为什么,但我觉得这与这个问题有关…所以我再解释一遍

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(也在文档中声明为WM_HOTKEY,例如,您可以在这里找到)是知道是否已按下注册热键:

当一个键被按下时,系统查找所有热键的匹配项。一旦找到匹配,系统将WM_HOTKEY消息发布到与热键相关联的窗口的消息队列中。如果热键没有与窗口相关联,则WM_HOTKEY消息被发布到与热键相关联的线程。


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

F12键一直保留给调试器使用,所以它不应该被注册为热键。即使不调试应用程序,F12也是保留的,以防内核模式调试器或即时调试器驻留。

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

看看我的KeyboardHandling项目在我的RocketLauncher GitHub爱好项目。你可以直接从里面取你需要的东西。我很快也会把它做成一个nuget包。