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

 

最后總結: 其實本方案監視熱鍵實現過程原理是:創建一個系統定時器線程,不斷的獲取每個按鍵的狀態是否被按下,如果被注冊的鍵狀態是被按下的 則回調傳入的事件來實現的。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM