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

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

任何人都可以帮我理解这些线条吗?

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

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

你是什​​么意思“它不起作用”? 您问题中的代码对我来说是正确的。

它可能无法工作的唯一原因是因为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()); } 

一旦完成,我怀疑你会看到一个exception被抛出错误信息:

热键已经注册

那么,这使调试问题变得更加简单,现在不是吧! 您可能需要选择不同的热键,因为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消息 。 只要按下您使用RegisterHotKeyfunction注册的热键, WM_HOTKEY消息就会自动发布到您的窗口。

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

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

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

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

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

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

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

 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 

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

0x0312WM_HOTKEY ,在此处可以找到的文档中也声明为WM_HOTKEY )是知道是否已按下已注册的热键

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


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

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