c# 快捷键模块

文章目录

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;

namespace HotKeyModule
{
    public static class Hotkey
    {
        private static readonly Dictionary<int, HotKeyCallbackHandler> keymap = new Dictionary<int, HotKeyCallbackHandler>();

        private static int nextHotkeyId = 10;
        private static readonly object keymapLock = new object();
        private static int GenerateHotkeyId() => Interlocked.Increment(ref nextHotkeyId);

        // 窗口消息处理
        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            const int WM_HOTKEY = 0x0312;
            if (msg == WM_HOTKEY)
            {
                int hotkeyId = wParam.ToInt32();
                if (keymap.TryGetValue(hotkeyId, out var callback))
                {
                    callback();
                    handled = true;
                }
            }
            return IntPtr.Zero;
        }

        // 获取窗口句柄和HwndSource
        private static (IntPtr hwnd, HwndSource hwndSource) GetWindowHandleAndSource(Window window)
        {
            var hwnd = new WindowInteropHelper(window).Handle;
            var hwndSource = HwndSource.FromHwnd(hwnd) ?? throw new InvalidOperationException("Failed to get HwndSource.");
            return (hwnd, hwndSource);
        }

        // 注册热键
        public static void Register(Window window, HotkeyModifiers modifiers, Key key, HotKeyCallbackHandler callback)
        {
            var (hwnd, hwndSource) = GetWindowHandleAndSource(window);
            hwndSource.AddHook(WndProc);

            int hotkeyId = GenerateHotkeyId();
            uint vk = (uint)KeyInterop.VirtualKeyFromKey(key);
            if (!RegisterHotKey(hwnd, hotkeyId, (uint)modifiers, vk))
            {
                throw new InvalidOperationException("Failed to register hotkey. Ensure the key combination is not already in use.");
            }

            lock (keymapLock)
            {
                keymap[hotkeyId] = callback;
            }
        }

        // 注销热键
        public static void Unregister(Window window, HotKeyCallbackHandler callback)
        {
            var (hwnd, _) = GetWindowHandleAndSource(window);

            lock (keymapLock)
            {
                foreach (var kvp in keymap)
                {
                    if (kvp.Value == callback)
                    {
                        UnRegisterHotKey(hwnd, kvp.Key);
                        keymap.Remove(kvp.Key);
                        break;
                    }
                }
            }
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnRegisterHotKey(IntPtr hWnd, int id);
    }

    // 热键回调委托
    public delegate void HotKeyCallbackHandler();

    public enum HotkeyModifiers
    {
        ALT = 0x0001,
        CTRL = 0x0002,
        SHIFT = 0x0004,
        WIN = 0x0008,
        CTRL_ALT = CTRL|ALT,
        CTRL_SHIFT = CTRL | SHIFT,
    }
}

命名空间和类

代码定义在一个名为 HotKeyModule 的命名空间中,其中包含一个静态类 Hotkey,用于管理热键的注册和注销。

类成员

静态成员
  1. keymap:

    • 类型:Dictionary<int, HotKeyCallbackHandler>
    • 用途:存储热键ID和对应的回调函数。
  2. nextHotkeyId:

    • 类型:int
    • 用途:生成唯一的热键ID。
    • 初始值:10
  3. keymapLock:

    • 类型:object
    • 用途:用于同步访问 keymap,确保线程安全。

静态方法

GenerateHotkeyId
  • 用途:生成唯一的热键ID。
  • 实现:使用 Interlocked.Increment 方法原子性地增加 nextHotkeyId,并返回其新的值。
WndProc
  • 用途:处理窗口消息,特别是热键消息。
  • 参数:
    • hwnd:窗口句柄
    • msg:消息类型
    • wParam:消息参数
    • lParam:消息参数
    • handled:是否处理了消息
  • 实现:
    • 检查消息类型是否为 WM_HOTKEY(0x0312)。
    • 如果是热键消息,从 wParam 中提取热键ID。
    • keymap 中查找对应的回调函数并执行。
    • handled 设置为 true,表示消息已处理。
GetWindowHandleAndSource
  • 用途:获取窗口句柄和 HwndSource
  • 参数:
    • window:需要处理的 Window 对象
  • 返回:
    • hwnd:窗口句柄
    • hwndSource:与窗口关联的 HwndSource
  • 实现:
    • 使用 WindowInteropHelper 获取窗口句柄。
    • 使用 HwndSource.FromHwnd 获取 HwndSource,如果获取失败则抛出异常。
Register
  • 用途:注册热键。
  • 参数:
    • window:需要注册热键的 Window 对象
    • modifiers:热键修饰符(例如 CTRLALT 等)
    • key:热键按键
    • callback:热键触发时的回调函数
  • 实现:
    • 调用 GetWindowHandleAndSource 获取窗口句柄和 HwndSource
    • WndProc 注册为窗口消息处理函数。
    • 生成唯一的热键ID。
    • 将按键转换为虚拟键码。
    • 调用 RegisterHotKey 函数注册热键,如果注册失败则抛出异常。
    • 将热键ID和回调函数添加到 keymap 中。
Unregister
  • 用途:注销热键。
  • 参数:
    • window:需要注销热键的 Window 对象
    • callback:需要注销的回调函数
  • 实现:
    • 调用 GetWindowHandleAndSource 获取窗口句柄和 HwndSource
    • 升级 keymapLock,确保线程安全。
    • keymap 中查找对应回调函数的热键ID。
    • 调用 UnRegisterHotKey 函数注销热键。
    • keymap 中移除对应的热键ID和回调函数。

静态方法(外部调用)

RegisterHotKeyUnRegisterHotKey
  • 用途:注册和注销热键的外部调用。
  • 参数:
    • hWnd:窗口句柄
    • id:热键ID
    • fsModifiers:热键修饰符
    • vk:虚拟键码
  • 实现:
    • 使用 DllImport 导入 user32.dll 中的 RegisterHotKeyUnRegisterHotKey 函数。

委托

HotKeyCallbackHandler
  • 用途:定义热键回调函数的委托。
  • 类型:public delegate void HotKeyCallbackHandler()

枚举

HotkeyModifiers
  • 用途:定义热键修饰符。
  • 成员:
    • ALT:0x0001
    • CTRL:0x0002
    • SHIFT:0x0004
    • WIN:0x0008
    • CTRL_ALTCTRL | ALT
    • CTRL_SHIFTCTRL | SHIFT

应用示例

这段代码是 OnSourceInitialized 方法的重写,用于在窗口的 SourceInitialized 事件触发时注册热键。SourceInitialized 事件在窗口句柄创建后立即触发。

csharp 复制代码
        protected override void OnSourceInitialized(EventArgs e)
        {
            Hotkey.Register(this, HotkeyModifiers.CTRL, Key.D, () =>
            {
                WindowController.Get<FlowView>().Hide();
                WindowController.Get<MainView>().Show();
            });

            Hotkey.Register(this, HotkeyModifiers.CTRL_ALT, Key.D, () =>
            {
                Application.Current.Shutdown();
            });
        }
相关推荐
GISer_Qing3 小时前
ASP.NET Core 8.0学习笔记(二十七)——数据迁移:Migrations深入与其他迁移命令
数据库·c#·.netcore·entityframework
追烽少年x4 小时前
C# WinForm 中的事件驱动模型
c#
CE贝多芬5 小时前
WPF的页面设计和实用功能实现
c#·wpf
code_shenbing6 小时前
WPF 实现虚拟键盘
c#·wpf
软件黑马王子11 小时前
C#初级教程(4)——流程控制:从基础到实践
开发语言·c#
水煮庄周鱼鱼16 小时前
C# 入门简介
开发语言·c#
软件黑马王子17 小时前
Unity游戏制作中的C#基础(6)方法和类的知识点深度剖析
开发语言·游戏·unity·c#
Nicole Potter18 小时前
请说明C#中的List是如何扩容的?
开发语言·面试·c#
gu2019 小时前
c#编程:学习Linq,重几个简单示例开始
开发语言·学习·c#·linq
pchmi1 天前
CNN常用卷积核
深度学习·神经网络·机器学习·cnn·c#