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.
148 lines
5.2 KiB
148 lines
5.2 KiB
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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|