You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
using System; using System.Runtime.InteropServices; using System.Windows.Forms;
namespace Apewer.WinForm {
/// <summary>热键。</summary>
public sealed class HotKey : IMessageFilter {
#region instance
IntPtr _pointer = IntPtr.Zero; Keys _key = Keys.None; ModifierKey _modifier = ModifierKey.None;
int _id = 0; Action<HotKey> _callback = null;
/// <summary>热键的标识符。 </summary>
public int Id { get => _id; }
/// <summary>修饰键。</summary>
public ModifierKey ModifierKey { get => _modifier; }
/// <summary>按键。</summary>
public Keys Key { get => _key; }
private HotKey(IntPtr pointer, Keys key, ModifierKey modifier, Action<HotKey> 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; }
/// <summary></summary>
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); }
/// <summary>释放热键。</summary>
public void Release() { UnregisterHotKey(Application.OpenForms[0].Handle, _id); }
#endregion
// Virtual-Key 代码
// https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes
/// <summary>绑定热键。</summary>
/// <param name="form">将接收热键生成的 WM_HOTKEY(0x0312)消息的窗口句柄。</param>
/// <param name="key">热键的虚拟键代码。</param>
/// <param name="modifier">修饰键。</param>
/// <param name="callback">回调。</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="Exception" />
public static HotKey Bind(Form form, Keys key, ModifierKey modifier, Action<HotKey> 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; }
/// <summary>绑定热键。</summary>
/// <remarks>WM_HOTKEY 消息将发布到调用线程的消息队列,并且必须在消息循环中进行处理。</remarks>
/// <param name="key">热键的虚拟键代码。</param>
/// <param name="modifier">修饰键。</param>
/// <param name="callback">回调。</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="Exception" />
public static HotKey Bind(Keys key, ModifierKey modifier, Action<HotKey> callback) { if (callback == null) throw new ArgumentNullException(nameof(callback));
var hotkey = new HotKey(IntPtr.Zero, key, modifier, callback); hotkey.Bind(); return hotkey; }
}
}
|