using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace Apewer.WinForm { /// 热键。 public sealed class HotKey : IMessageFilter { #region instance IntPtr _pointer = IntPtr.Zero; Keys _key = Keys.None; ModifierKey _modifier = ModifierKey.None; int _id = 0; Action _callback = null; /// 热键的标识符。 public int Id { get => _id; } /// 修饰键。 public ModifierKey ModifierKey { get => _modifier; } /// 按键。 public Keys Key { get => _key; } private HotKey(IntPtr pointer, Keys key, ModifierKey modifier, Action callback) { if (callback == null) throw new ArgumentNullException(nameof(callback), "回调函数不能为空。"); switch (key) { case Keys.KeyCode: case Keys.Modifiers: case Keys.None: case Keys.Shift: case Keys.Control: case Keys.Alt: throw new ArgumentException($"按键【{key}】无法注册为热键。"); } // 应用程序必须在0x0000到0xBFFF的范围内指定 ID 值。 // 共享 DLL 必须通过 0xFFFF(GlobalAddAtom 函数返回的范围)指定0xC000区域中的值。 _id = ((int)modifier << 16) & (int)key; _pointer = pointer; _callback = callback; _key = key; _modifier = modifier; } /// public override string ToString() { var alt = (ModifierKey & ModifierKey.ALT) == ModifierKey.ALT ? "ALT + " : ""; var ctrl = (ModifierKey & ModifierKey.CTRL) == ModifierKey.CTRL ? "CTRL + " : ""; var shift = (ModifierKey & ModifierKey.SHIFT) == ModifierKey.SHIFT ? "SHIFT + " : ""; var win = (ModifierKey & ModifierKey.WIN) == ModifierKey.WIN ? "WIN + " : ""; var result = $"{alt}{ctrl}{shift}{win}{Key}"; return result; } #endregion #region bind const int WM_HOTKEY = 0x0312; [DllImport("user32.dll")] static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, Keys vk); [DllImport("user32.dll")] static extern bool UnregisterHotKey(IntPtr hWnd, int id); bool IMessageFilter.PreFilterMessage(ref Message m) { if (m.Msg == WM_HOTKEY && (int)m.WParam == _id) { _callback?.Invoke(this); return true; } return false; } void Bind() { var success = RegisterHotKey(_pointer, _id, (int)_modifier, _key); if (!success) { var error = Marshal.GetLastWin32Error(); if (error == 1409) throw new Exception($"热键【{ToString()}】已被占用。"); throw new Exception($"错误 {error}:注册热键【{ToString()}】失败。"); } Application.AddMessageFilter(this); } /// 释放热键。 public void Release() { UnregisterHotKey(Application.OpenForms[0].Handle, _id); } #endregion // Virtual-Key 代码 // https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes /// 绑定热键。 /// 将接收热键生成的 WM_HOTKEY(0x0312)消息的窗口句柄。 /// 热键的虚拟键代码。 /// 修饰键。 /// 回调。 /// /// /// public static HotKey Bind(Form form, Keys key, ModifierKey modifier, Action callback) { if (form == null) throw new ArgumentNullException(nameof(form)); if (callback == null) throw new ArgumentNullException(nameof(callback)); var hotkey = new HotKey(form.Handle, key, modifier, callback); hotkey.Bind(); return hotkey; } /// 绑定热键。 /// WM_HOTKEY 消息将发布到调用线程的消息队列,并且必须在消息循环中进行处理。 /// 热键的虚拟键代码。 /// 修饰键。 /// 回调。 /// /// /// public static HotKey Bind(Keys key, ModifierKey modifier, Action callback) { if (callback == null) throw new ArgumentNullException(nameof(callback)); var hotkey = new HotKey(IntPtr.Zero, key, modifier, callback); hotkey.Bind(); return hotkey; } } }