【问题标题】:Capturing a key without focusing the window在不聚焦窗口的情况下捕获键
【发布时间】:2013-03-15 09:18:41
【问题描述】:

我有一个应用程序,它总是检查是否按下了像 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);
    }

我在RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F12);这一行设置了0,这样只有按下F12才会捕获。

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

这里有些行我看不懂:

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

谁能帮我理解这些台词?

【问题讨论】:

    标签: c# keyboard-hook global-hotkey


    【解决方案1】:

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

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

    它可能无法正常工作的唯一原因是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。只要按下您使用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 的处理)。完成附加逻辑后,您希望继续处理基类的逻辑,因此继续转发消息。

    【讨论】:

    • 废话,当我真正为自己运行代码时,解释变得更加清晰。答案已更新。 :-)
    【解决方案2】:

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

    【讨论】:

    • 注意:如果您更改 ShowInTaskbar 属性,因为窗口被破坏并注册了 HotKey,它也可能不起作用。刚刚发现了困难的方法。
    【解决方案3】:

    我不知道为什么,但我觉得这与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(在文档中也声明为WM_HOTKEY 例如here)是为了知道是否已按下注册的热键

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


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

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

    【讨论】:

    • 很棒的解释,!不知道为什么我是第一个对此表示赞同的人,这个答案值得更多。
    【解决方案4】:

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

    在我的RocketLauncher GitHub hobby project 中查看my KeyboardHandling project。你可以直接从中获取你需要的东西。我也打算很快将它变成一个 nuget 包。

    【讨论】:

    • 但是如果我改变 RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 2, (int) Keys.F12) 并使用 Ctrl+F10 即使它不是焦点也可以工作。
    • 很公平 - 我假设它与在设计师中设置它们相同。
    • 强烈赞成使用通过RegisterHotKey注册的全局热键而不是全局键盘挂钩。具有全局作用域的钩子非常沉重和昂贵,因此应该只在绝对需要的地方使用。这里不是这种情况(虽然我不确定你的启动器应用程序,但它可能也不存在)。
    猜你喜欢
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 2020-03-23
    • 2010-12-28
    相关资源
    最近更新 更多