首页 > 系统相关 >C# 封装 Windows 全局热键

C# 封装 Windows 全局热键

时间:2023-12-28 09:22:51浏览次数:27  
标签:C# 热键 VirtualKey Windows int hotkeyId ModifiedKeys public

全局热键工具类(GlobalHotkey)

【文 / 张赐荣】

1. 功能概述

全局热键工具类(GlobalHotkey)是一个用于注册全局热键的工具类。它允许你在你的应用程序中注册特定的键盘组合,以便在用户按下这些组合时触发相应的事件。此工具类提供了以下主要功能:

  • 注册多个热键并定义每个热键的组合。
  • 当用户按下注册的热键时,触发相应的事件。
  • 跟踪热键按下的次数。

2. 如何使用

a. 注册热键

要注册热键,可以使用 RegisterHotkey 方法,该方法允许你指定修改键(如 Ctrl、Shift、Alt、Windows)和虚拟键(如 A、B、数字键等)的组合。示例:

int hotkeyId = GlobalHotkey.RegisterHotkey(ModifiedKeys.Ctrl | ModifiedKeys.Alt, VirtualKey.A);
if (hotkeyId > 0)
{
    // 热键注册成功,可以执行相应操作
}

b. 注销热键

要注销之前注册的热键,可以使用 UnregisterHotkey 方法,并传入先前注册热键时返回的热键ID。示例:

bool isUnregistered = GlobalHotkey.UnregisterHotkey(hotkeyId);
if (isUnregistered)
{
    // 热键注销成功
}

3. 多个热键绑定

可以多次调用 RegisterHotkey 方法来注册多个热键,每个热键都应具有唯一的热键ID。示例:

int hotkeyId1 = GlobalHotkey.RegisterHotkey(ModifiedKeys.Ctrl, VirtualKey.F1);
int hotkeyId2 = GlobalHotkey.RegisterHotkey(ModifiedKeys.Alt, VirtualKey.F2);
// 可以继续注册其他热键

4. 判断按键次数

当热键被按下时,会触发 HotkeyPressedEvent 事件。可以订阅这个事件来处理热键按下的情况,并获取按键的计数。示例:

GlobalHotkey.HotkeyPressedEvent += (sender, e) =>
{
    // 处理按键事件,可以通过 HotkeyPressedEventArgs 获取按键相关信息
    Console.WriteLine($"热键按下:{e.VirtualKey},按键次数:{e.PressedCount}");
};

5. 注意事项

  • 在注册热键之前,确保你的应用程序处于消息循环状态(MessageLoop)。
  • 每个热键的组合应该是独一无二的,避免冲突。
  • 在不再需要使用热键时,及时进行注销操作。

6. 示例代码

下面是一个简单的示例代码,演示了如何注册热键并处理热键按下事件:

// 注册热键
int hotkeyId = GlobalHotkey.RegisterHotkey(ModifiedKeys.Ctrl, VirtualKey.F1);

// 处理热键按下事件
GlobalHotkey.HotkeyPressedEvent += (sender, e) =>
{
    if (e.HotkeyId == hotkeyId)
    {
        // 处理 Ctrl + F1 按下事件
        Console.WriteLine($"热键按下:{e.VirtualKey},按键次数:{e.PressedCount}");
    }
};

全局热键工具类完整代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Dotnet.WinAPI.GlobalHotkey
{
	public delegate void HotkeyPressedEventHandler(object sender, HotkeyPressedEventArgs e);

	public static class GlobalHotkey
	{
		private static HotkeyMessageFilter _HotkeyMessageFilter = null;
		private static readonly Dictionary<int, KeyData> HotkeyData = new Dictionary<int, KeyData>();

		public static event HotkeyPressedEventHandler HotkeyPressedEvent;

		[DllImport("user32.dll", EntryPoint = "RegisterHotKey")]
		private static extern bool API_RegisterHotKey(IntPtr hWnd, int id, ModifiedKeys fsModifiers, VirtualKey vk);

		[DllImport("user32.dll", EntryPoint = "UnregisterHotKey")]
		private static extern bool API_UnregisterHotKey(IntPtr hWnd, int id);

		public static int RegisterHotkey(ModifiedKeys mks, VirtualKey vk)
		{
			try
			{
				if (mks == ModifiedKeys.None && vk == VirtualKey.None)
				{
					return (-1);
				}
				if (!Application.MessageLoop)
				{
					return (-2);
				}
				if (_HotkeyMessageFilter is null)
				{
					_HotkeyMessageFilter = new HotkeyMessageFilter();
					Application.AddMessageFilter(_HotkeyMessageFilter);
				}
				int hId = (((byte)mks) << 8) | (byte)vk;
				UnregisterHotkey(hId);
				int result = (API_RegisterHotKey(IntPtr.Zero, hId, mks, vk) ? hId : -1);
				if (result > 0)
				{
					System.Windows.Forms.Timer keyTimer = new System.Windows.Forms.Timer() { Interval = SystemInformation.DoubleClickTime };
					KeyData keyData = new KeyData() { KeyPressedCount = 0, KeyTimer = keyTimer };
					HotkeyData[hId] = keyData;
					keyTimer.Tick += (_s, _e) =>
					{
						keyTimer.Stop();
						keyData.KeyPressedCount = 0;
					};
				}
				return (result);
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex);
				return (-3);
			}
		}

		public static bool UnregisterHotkey(int hotkeyId)
		{
			try
			{
				bool res = API_UnregisterHotKey(IntPtr.Zero, hotkeyId);
				if (res && HotkeyData.ContainsKey(hotkeyId))
				{
					HotkeyData[hotkeyId].KeyTimer.Stop();
					HotkeyData.Remove(hotkeyId);
				}
				return (res);
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex);
				return (false);
			}
		}

		class HotkeyMessageFilter : IMessageFilter
		{
			private const int WM_HOTKEY = 786;

			bool IMessageFilter.PreFilterMessage(ref Message m)
			{
				switch (m.Msg)
				{
					case WM_HOTKEY:
						try
						{
							int hId = m.WParam.ToInt32();
							if (!(HotkeyPressedEvent is null) && HotkeyData.ContainsKey(hId))
							{
								HotkeyData[hId].KeyTimer.Start();
								HotkeyData[hId].KeyPressedCount++;
								int lParam = m.LParam.ToInt32();
								byte vk = (byte)(lParam >> 16);
								byte mks = (byte)(lParam & 0Xffff);
								HotkeyPressedEvent?.Invoke(this, new HotkeyPressedEventArgs(hId, (ModifiedKeys)mks, (VirtualKey)vk, HotkeyData[hId].KeyPressedCount));
							}
						}
						catch (Exception ex)
						{
							Debug.WriteLine(ex);
						}
						return (true);
						break;
				}
				return (false);
			}
		}
	}

	public class HotkeyPressedEventArgs : EventArgs
	{
		public int HotkeyId { get; }
		public ModifiedKeys ModifiedKeys { get; }
		public VirtualKey VirtualKey { get; }
		public int PressedCount { get; }

		public HotkeyPressedEventArgs(int hotkeyId, ModifiedKeys modifiedKeys, VirtualKey virtualKey, int pressedCount)
		{
			this.HotkeyId = hotkeyId;
			this.ModifiedKeys = ModifiedKeys;
			this.VirtualKey = virtualKey;
			this.PressedCount = pressedCount;
		}

		public override string ToString()
		{
			return ($"{nameof(this.HotkeyId)}:{this.HotkeyId}, {nameof(this.ModifiedKeys)}:{this.ModifiedKeys}, {nameof(this.VirtualKey)}:{this.VirtualKey}, {nameof(this.PressedCount)}:{this.PressedCount}");
		}
	}

	class KeyData
	{
		public int KeyPressedCount = 0;
		public System.Windows.Forms.Timer KeyTimer = null;
	}

	[Flags]
	public enum ModifiedKeys : byte
	{
		None = 0,
		Alt = 1,
		Ctrl = 2,
		Shift = 4,
		Windows = 8
	}

	public enum VirtualKey : uint
	{
		None = 0,
		BackSpace = 8,
		Tab = 9,
		Clear = 12,
		Enter = 13,
		Pause = 19,
		Caps = 20,
		Escape = 27,
		Space = 32,
		PageUp = 33,
		PageDown = 34,
		End = 35,
		Home = 36,
		LeftArrow = 37,
		UpArrow = 38,
		RightArrow = 39,
		DownArrow = 40,
		PrintScreen = 44,
		Insert = 45,
		Delete = 46,
		Zero = 48,
		One = 49,
		Two = 50,
		Three = 51,
		Four = 52,
		Five = 53,
		Six = 54,
		Seven = 55,
		Eight = 56,
		Nine = 57,
		A = 65,
		B = 66,
		C = 67,
		D = 68,
		E = 69,
		F = 70,
		G = 71,
		H = 72,
		I = 73,
		J = 74,
		K = 75,
		L = 76,
		M = 77,
		N = 78,
		O = 79,
		P = 80,
		Q = 81,
		R = 82,
		S = 83,
		T = 84,
		U = 85,
		V = 86,
		W = 87,
		X = 88,
		Y = 89,
		Z = 90,
		Apps = 93,
		NumPad0 = 96,
		NumPad1 = 97,
		NumPad2 = 98,
		NumPad3 = 99,
		NumPad4 = 100,
		NumPad5 = 101,
		NumPad6 = 102,
		NumPad7 = 103,
		NumPad8 = 104,
		NumPad9 = 105,
		F1 = 112,
		F2 = 113,
		F3 = 114,
		F4 = 115,
		F5 = 116,
		F6 = 117,
		F7 = 118,
		F8 = 119,
		F9 = 120,
		F10 = 121,
		F11 = 122,
		F12 = 123,
		NumLock = 144,
		ScrollLock = 145,
		Semicolon = 186,
		Equal = 187,
		Comma = 188,
		Minus = 189,
		Dot = 190,
		Slash = 191,
		BackQuote = 192,
		LeftBracket = 219,
		BackSlash = 220,
		RightBracket = 221,
		Quote = 222,
	}
}

【本文作者 张赐荣】

标签:C#,热键,VirtualKey,Windows,int,hotkeyId,ModifiedKeys,public
From: https://www.cnblogs.com/netlog/p/17931935.html

相关文章

  • 「题解」Codeforces 1427G One Billion Shades of Grey
    感谢127的指导/ll\(|h_u-h_v|=\max(0,h_u-h_v)+\max(0,h_v-h_u)\),那么可以把它看成这样的问题:\[\min\{\sum_{(u,v)}\max(0,h_u-h_v+w_{u,v})c_{u,v}\}\]对偶一下,问题就变为:如果两个格子相邻就互相连容量为\(c_{u,v}=1\),费用为\(w_{u,v}=0\)的边,跑最大费用循环流。为了限......
  • 南阳 南阳农业职业学院(Nanyang Vocational College of Agriculture)
    南阳农业职业学院(NanyangVocationalCollegeofAgriculture),位于河南省南阳市,简称“南阳农职院”,是经河南省人民政府批准、教育部备案的一所全日制普通高等职业院校,是河南省特色高职院校。学校前身是1951年创办的年的南阳农业学校,1958年升格为南阳农学院,1959年改为南阳农业专科......
  • [LeetCode] 1578. Minimum Time to Make Rope Colorful
    Alicehasnballoonsarrangedonarope.Youaregivena0-indexedstringcolorswherecolors[i]isthecoloroftheithballoon.Alicewantstheropetobecolorful.Shedoesnotwanttwoconsecutiveballoonstobeofthesamecolor,sosheasksBobfor......
  • 【scikit-learn基础】--『监督学习』之 LASSO回归
    LASSO(LeastAbsoluteShrinkageandSelectionOperator)回归模型一般都是用英文缩写表示,硬要翻译的话,可翻译为最小绝对收缩和选择算子。它是一种线性回归模型的扩展,其主要目标是解决高维数据中的特征选择和正则化问题。1.概述在LASSO中,通过使用L1正则化项,它能够在回归系数中......
  • 03 CP2104串口驱动安装
    1概述串口是最常用的一种调试工具,开发过程中我们经常会使用串口输出一些调试信息,在LINUX下也会用串口控制台控制LINUX系统。目前的串口,大部分都是USB转串口。CP2104是一款非常稳定好用的USB转串口芯片。接下来我们看下如何进行驱动安装。2软件下载登录米联客技术论坛https://......
  • 河南警察学院 Henan police college
    河南警察学院是我省唯一的省属公安本科院校,前身是1949年2月成立的中共豫西区委保卫干部训练班,历经河南省公安干部学校、河南省人民警察学校、河南公安高等专科学校等时期。2010年3月经教育部批准成立河南警察学院。2012年8月开封警校、洛阳警校并入河南警察学院。2019年11月通过教......
  • 【Datahub系列教程】Datahub入门必学——DatahubCLI之Docker命令详解
    大家好,我是独孤风,今天的元数据管理平台Datahub的系列教程,我们来聊一下DatahubCLI。也就是Datahub的客户端。我们在安装和使用Datahub的过程中遇到了很多问题。如何安装Datahub?为什么总是拉取镜像?如何启动Datahub?这些Datahub的Docker命令都是做什么的?有很多同学虽然搜......
  • CF1910G Pool Records记录
    题目链接:https://codeforces.com/contest/1910/problem/G题意简述有两个运动员以未知的固定速度\(v_1\nev_2\)在一个长为\(50\)米的游泳池中游泳,一旦到边缘就立即掉头。现在有他们前\(n\)次相遇时间\(t_i\)(递增,均为整数)的记录,问这个记录是否合法。\(n\le2\times10^......
  • 基于FPGA的图像差分运算及目标提取实现,包含testbench和MATLAB辅助验证程序
    1.算法运行效果图预览  2.算法运行软件版本matlab2022a 3.算法理论概述      基于FPGA(Field-ProgrammableGateArray)的图像差分运算及目标提取实现主要涉及图像处理、差分运算和目标提取等原理和数学公式。 一、图像处理原理       图像处理是......
  • C++ --- 函数模板
    函数模板C++的一种编程思想称为泛型编程,主要利用的技术就是模板。编写与类型无关的调用代码,是代码复用的一种手段。 模板是泛型编程的基础。C++提供两种模板机制:函数模板和类模板。函数模板:建立一个通用的函数,它用到的参数类型可以不确定,用一个虚拟类型替代。等到函数调用的时......