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;
}
}
}