C# 实现全局监视热键( 键盘按下事件)

C# 实现全局监控键盘点击事件

 

记录一下实现在C#程序以外的界面也能实现键盘按下并执行对应的事件的实现方式。

 

  由于公司有一个项目,需要注册热键来实现全局检测按键才能完成该功能。 winfrom中的键盘点击事件又只能焦点在程序窗口上才能实现,这种达不到我想要的效果。

  我在网上找了很多案例都让我不是很满意,效果也不是特别好。 无意间从一个论坛中找到一个易语言编写的监视热键编译好的模块,但C#并不能直接调用这个模块,我就创建了一个易语言程序,并把调用这个易模块然后编译为dll后给C#调用。然后我发现效果还是挺好的。但是两个弊端:

  1.易语言编译的文件只支持32位的(C#程序64位就无法调用)。 *问题最严重

  2.总觉得一个基础的功能 还必须依赖外部dll 个人心里感受不是那么好,而且这个也无法直接集成到项目代码去,dll依赖太强了。。 

  由于第一个原因,我的程序是64位的,这个模块监视热键又确实挺好用,我就想着把他翻译成C#代码,就方便使用了。然后就索性在论坛找了个工具,把这个易语言模块给反编译了,看下他内部逻辑咋写的。

  这是反编译前的易模块:

  

 

  反编译后:

  

 

 

  打开文件,查看代码 (反编译后虽然逻辑代码都出来了,但是变量命名就是乱的,看的我眼花缭乱):

  这些是一部分逻辑代码。

 

 

 

 

 

 

  接下来就开始干活,把它翻译成C#语言来实现此功能,之后就想怎么用就怎么用了。

 

一、创建一个Win32Api类

 

 

 

 

 二、定义windowsApi

 1         /// <summary>
 2         /// 创建一个定时器
 3         /// </summary>
 4         /// <param name="hWnd">程序句柄,为空则为系统级定时器</param>
 5         /// <param name="nIDEvent">定时器ID</param>
 6         /// <param name="uElapse">毫秒周期</param>
 7         /// <param name="lpTimerFunc">定时器触发事件</param>
 8         /// <returns></returns>
 9         [DllImport("user32.dll")]
10         private static extern int SetTimer(int hWnd, int nIDEvent, int uElapse, Action lpTimerFunc);
11 
12         /// <summary>
13         /// 销毁定时器
14         /// </summary>
15         /// <param name="hWnd">程序句柄,为空则为系统级定时器</param>
16         /// <param name="nIDEvent">定时器ID,  若SetTimer的hWnd为0,则必须传SetTimer的返回值</param>
17         /// <returns></returns>
18         [DllImport("user32.dll")]
19         private static extern int KillTimer(int hWnd, int nIDEvent);
20 
21         /// <summary>
22         /// 确定在调用函数时某个键是向上还是向下,以及在上一次调用GetAsyncKeyState之后是否按下了该键。
23         /// </summary>
24         /// <param name="keyCode"></param>
25         /// <returns></returns>
26         [DllImport("user32.dll")]
27         private static extern Int16 GetAsyncKeyState(int keyCode);
28 
29         /// <summary>
30         /// 将消息信息传递给指定的窗口过程。 回调钩子
31         /// </summary>
32         /// <param name="lpPrevWndFunc"></param>
33         /// <param name="hWnd"></param>
34         /// <param name="Msg"></param>
35         /// <param name="wParam"></param>
36         /// <param name="lParam"></param>
37         /// <returns></returns>
38         [DllImport("user32.dll")]
39         private static extern int CallWindowProcA(Action lpPrevWndFunc, int hWnd, int Msg, int wParam, int lParam);
40 
41         /// <summary>
42         /// 关闭打开的对象句柄。
43         /// </summary>
44         /// <param name="hObject"></param>
45         /// <returns></returns>
46         [DllImport("kernel32.dll")]
47         private static extern int CloseHandle(int hObject);
48 
49         /// <summary>
50         /// 创建一个线程以在调用进程的虚拟地址空间内执行。
51         /// </summary>
52         /// <param name="lpThreadAttributes"></param>
53         /// <param name="dwStackSize"></param>
54         /// <param name="lpStartAddress"></param>
55         /// <param name="lpParameter"></param>
56         /// <param name="dwCreationFlags"></param>
57         /// <param name="lpThreadId"></param>
58         /// <returns></returns>
59         [DllImport("kernel32.dll")]
60         private static extern int CreateThread(int lpThreadAttributes, int dwStackSize, Action lpStartAddress, int lpParameter, int dwCreationFlags, int lpThreadId);

 

三、定义一个热键信息类

 1         /// <summary>
 2         /// 热键信息
 3         /// </summary>
 4         public class Hotkey
 5         {
 6             public int Id { get; set; }
 7 
 8             public Action Action { get; set; }
 9 
10             public int KeyCode { get; set; }
11 
12             public int FunKeyCode { get; set; }
13 
14             public int OtherKeyCode { get; set; }
15 
16             public byte KeyState { get; set; }
17 
18             public bool State { get; set; }
19 
20             public bool DirectTrigger { get; set; }
21         }

 

三、定义两个静态对象:事件、注册的热键集合 (必须为静态全局对象,防止被GC回收)

1         private static List<Hotkey> hotkeyList = null; //记录注册的热键信息
2         private static Action _HotkeyAction = null;  //监视热键线程

 

四、封装方法供外部调用实现热键注册

 1         /// <summary>
 2         /// 注册热键
 3         /// </summary>
 4         /// <param name="action">响应事件</param>
 5         /// <param name="keyCode">键代码</param>
 6         /// <param name="funKeyCode">功能键代码  1 Alt  2 Ctrl  4 Shift  8 Win 若要两个或以上的状态键,则把它们的值相加.</param>
 7         /// <param name="otherKeyCode">如果需要注册由两个普通键组合的热键,可设置一个其它键代码.</param>
 8         /// <param name="millisecondsTimeout">默认为10,监视热键的周期时间(建议5-200之间)</param>
 9         /// <param name="DirectTrigger">默认为false:创建新的线程事件 true:直接调用事件等待返回</param>
10         /// <returns></returns>
11         public static int RegisterHotkey(Action action, int keyCode, int funKeyCode = 0, int otherKeyCode = 0, int millisecondsTimeout = 10, bool DirectTrigger = false)
12         {
13             Hotkey hotkey = new Hotkey();
14 
15             if (keyCode <= 0)
16                 return 0;
17             if (hotkeyList == null)
18                 hotkeyList = new List<Hotkey>();
19 
20             for (int i = 0; i < hotkeyList.Count; i++)
21             {
22                 if (hotkeyList[i].KeyCode == keyCode && hotkeyList[i].FunKeyCode == funKeyCode && hotkeyList[i].OtherKeyCode == otherKeyCode)
23                 {
24                     hotkeyList[i].Action = action;
25                     hotkeyList[i].DirectTrigger = DirectTrigger;
26 
27                     if (hotkeyList[i].Id != 0)
28                     {
29                         return hotkeyList[i].Id;
30                     }
31 
32                     hotkeyList[i].Id = i + 1000000;
33                     return hotkeyList[i].Id;
34                 }
35             }
36 
37             hotkey.Action = action;
38             hotkey.KeyCode = keyCode;
39             hotkey.FunKeyCode = funKeyCode;
40             hotkey.OtherKeyCode = otherKeyCode;
41             hotkey.DirectTrigger = DirectTrigger;
42             hotkey.Id = hotkeyList.Count + 1000001;
43             hotkeyList.Add(hotkey);
44 
45             if (hotkey.Id == 1000001)
46             {
47                 _HotkeyAction = MonitorHotkeyThreads;
48 
49                 int time = millisecondsTimeout == 0 ? 10 : millisecondsTimeout;
50 
51                 // 创建定时器
52                 SetTimer(_HotkeyAction, time);
53             }
54 
55             return hotkey.Id;
56         }

 

五、创建监视注册热键的线程方法

 1        /// <summary>
 2         /// 监视注册热键的线程
 3         /// </summary>
 4         public static void MonitorHotkeyThreads()
 5         {
 6             Action tempAction = null;
 7             int tempId = 0;
 8 
 9             Int16[] cacheKeyState = new Int16[256];
10 
11             for (int i = 0; i < 255; i++)
12             {
13                 cacheKeyState[i] = 251;
14                 cacheKeyState[i] = GetAsyncKeyState(i);
15             }
16 
17             for (int i = 0; i < hotkeyList.Count; i++)
18             {
19                 if (hotkeyList[i].Id != 0)
20                 {
21                     int k = hotkeyList[i].KeyCode;
22                     k = cacheKeyState[k];
23 
24                     if (k == 0)  //0表示无状态
25                     {
26                         if (hotkeyList[i].KeyState == 1)
27                         {
28                             hotkeyList[i].KeyState = 2;
29                         }
30                         else
31                         {
32                             hotkeyList[i].KeyState = 0;
33                         }
34                         continue;
35                     }
36                     if (k < 0)  //-32767,按下状态
37                     {
38                         if (hotkeyList[i].KeyState == 0)
39                         {
40                             hotkeyList[i].KeyState = 1;
41                         }
42                         if (hotkeyList[i].KeyCode < 0)
43                         {
44                             continue;
45                         }
46                     }
47 
48                     // 判断激活热键
49                     if (hotkeyList[i].KeyState > 0 && hotkeyList[i].KeyState != 88)
50                     {
51                         hotkeyList[i].KeyState = 88;
52 
53                         int funNum = cacheKeyState[18] < 0 ? 1 : 0;
54                         funNum += cacheKeyState[17] < 0 ? 2 : 0;
55                         funNum += cacheKeyState[16] < 0 ? 4 : 0;
56                         funNum += cacheKeyState[91] < 0 ? 8 : 0;
57 
58                         if (hotkeyList[i].FunKeyCode == funNum)
59                         {
60                             if (hotkeyList[i].OtherKeyCode != 0)
61                             {
62                                 k = hotkeyList[i].OtherKeyCode;
63                                 if (cacheKeyState[k] >= 0)
64                                 {
65                                     continue;
66                                 }
67                             }
68 
69                             tempAction = hotkeyList[i].Action;
70                             tempId = hotkeyList[i].Id;
71 
72                             if (hotkeyList[i].DirectTrigger)
73                             {
74                                 CallWindowProcA(tempAction, tempId, 0, 0, 0);
75                             }
76                             else
77                             {
78                                 CloseHandle(CreateThread(0, 0, tempAction, tempId, 0, 0));
79                             }
80                         }
81                     }
82 
83                 }
84             }
85         }

 

六、封装撤销监视热键的方法

 1         /// <summary>
 2         /// 撤销监视热键
 3         /// </summary>
 4         /// <param name="id">撤消的热键标识(注册时的返回值) ,如果留空则撤消全部热键</param>
 5         /// <returns></returns>
 6         public static bool UndoMonitorHotkey(int id)
 7         {
 8             if (hotkeyList != null && hotkeyList.Count > 0)
 9             {
10                 for (int i = 0; i < hotkeyList.Count; i++)
11                 {
12                     if (id == 0)
13                     {
14                         hotkeyList[i].Id = 0;
15                     }
16                     else
17                     {
18                         if (id == hotkeyList[i].Id)
19                         {
20                             hotkeyList[i].Id = 0;
21                             return true;
22                         }
23                     }
24                 }
25             }
26             return id == 0;
27         }

 

 

------------------------------------------------------------------功能代码已完成,下面开始调用---------------------------------------------------------------------------------------

 

七、调用

点击注册热键

 

 键盘按下字母键盘 1  (在桌面任何一个界面都可实现监控)

 

 

 撤销热键就是用注册时的返回值传入封装的UndoMonitorHotkey() 即可撤销,我这里就不演示了。

 

 附上一个键码对照表地址:http://www.atoolbox.net/Tool.php?Id=815

 

最后总结: 其实本方案监视热键实现过程原理是:创建一个系统定时器线程,不断的获取每个按键的状态是否被按下,如果被注册的键状态是被按下的 则回调传入的事件来实现的。

 

  

 

posted @ 2022-04-20 22:45  阿东呢  阅读(5262)  评论(1编辑  收藏  举报