C#全局键盘监听(Hook)的使用(转载)
生活随笔
收集整理的這篇文章主要介紹了
C#全局键盘监听(Hook)的使用(转载)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一.為什么需要全局鍵盤監聽?
在某些情況下應用程序需要實現快捷鍵執行特定功能,例如大家熟知的QQ截圖功能Ctrl+Alt+A快捷鍵,只要QQ程序在運行(無論是擁有焦點還是處于后臺運行狀態),都可以按下快捷鍵使用此功能…
這個時候在程序中添加鍵盤監聽肯定不能滿足需求了,當用戶焦點不在App上時(如最小化,或者用戶在處理其它事物等等)鍵盤監聽就失效了
二.怎樣才能實現全局鍵盤監聽?
這里需要用到Windows API,源碼如下:(可以作為一個工具類[KeyboardHook.cs]收藏起來)
1 using System; 2 using System.Runtime.InteropServices; 3 using System.Windows.Forms; 4 //using System.Windows.Input; 5 6 namespace WpfApplication1 7 { 8 /// <summary> 9 /// 鍵盤鉤子 10 /// [以下代碼來自某網友,并非本人原創] 11 /// </summary> 12 class KeyboardHook 13 { 14 public event System.Windows.Forms.KeyEventHandler KeyDownEvent; 15 public event KeyPressEventHandler KeyPressEvent; 16 public event System.Windows.Forms.KeyEventHandler KeyUpEvent; 17 18 public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); 19 static int hKeyboardHook = 0; //聲明鍵盤鉤子處理的初始值 20 //值在Microsoft SDK的Winuser.h里查詢 21 // http://www.bianceng.cn/Programming/csharp/201410/45484.htm 22 public const int WH_KEYBOARD_LL = 13; //線程鍵盤鉤子監聽鼠標消息設為2,全局鍵盤監聽鼠標消息設為13 23 HookProc KeyboardHookProcedure; //聲明KeyboardHookProcedure作為HookProc類型 24 //鍵盤結構 25 [StructLayout(LayoutKind.Sequential)] 26 public class KeyboardHookStruct 27 { 28 public int vkCode; //定一個虛擬鍵碼。該代碼必須有一個價值的范圍1至254 29 public int scanCode; // 指定的硬件掃描碼的關鍵 30 public int flags; // 鍵標志 31 public int time; // 指定的時間戳記的這個訊息 32 public int dwExtraInfo; // 指定額外信息相關的信息 33 } 34 //使用此功能,安裝了一個鉤子 35 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 36 public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); 37 38 39 //調用此函數卸載鉤子 40 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 41 public static extern bool UnhookWindowsHookEx(int idHook); 42 43 44 //使用此功能,通過信息鉤子繼續下一個鉤子 45 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 46 public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); 47 48 // 取得當前線程編號(線程鉤子需要用到) 49 [DllImport("kernel32.dll")] 50 static extern int GetCurrentThreadId(); 51 52 //使用WINDOWS API函數代替獲取當前實例的函數,防止鉤子失效 53 [DllImport("kernel32.dll")] 54 public static extern IntPtr GetModuleHandle(string name); 55 56 public void Start() 57 { 58 // 安裝鍵盤鉤子 59 if (hKeyboardHook == 0) 60 { 61 KeyboardHookProcedure = new HookProc(KeyboardHookProc); 62 hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0); 63 //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); 64 //************************************ 65 //鍵盤線程鉤子 66 //SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要監聽的線程idGetCurrentThreadId(), 67 //鍵盤全局鉤子,需要引用空間(using System.Reflection;) 68 //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0); 69 // 70 //關于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函數將鉤子加入到鉤子鏈表中,說明一下四個參數: 71 //idHook 鉤子類型,即確定鉤子監聽何種消息,上面的代碼中設為2,即監聽鍵盤消息并且是線程鉤子,如果是全局鉤子監聽鍵盤消息應設為13, 72 //線程鉤子監聽鼠標消息設為7,全局鉤子監聽鼠標消息設為14。lpfn 鉤子子程的地址指針。如果dwThreadId參數為0 或是一個由別的進程創建的 73 //線程的標識,lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可以指向當前進程的一段鉤子子程代碼。鉤子函數的入口地址,當鉤子鉤到任何 74 //消息后便調用這個函數。hInstance應用程序實例的句柄。標識包含lpfn所指的子程的DLL。如果threadId 標識當前進程創建的一個線程,而且子 75 //程代碼位于當前進程,hInstance必須為NULL。可以很簡單的設定其為本應用程序的實例句柄。threaded 與安裝的鉤子子程相關聯的線程的標識符 76 //如果為0,鉤子子程與所有的線程關聯,即為全局鉤子 77 //************************************ 78 //如果SetWindowsHookEx失敗 79 if (hKeyboardHook == 0) 80 { 81 Stop(); 82 throw new Exception("安裝鍵盤鉤子失敗"); 83 } 84 } 85 } 86 public void Stop() 87 { 88 bool retKeyboard = true; 89 90 91 if (hKeyboardHook != 0) 92 { 93 retKeyboard = UnhookWindowsHookEx(hKeyboardHook); 94 hKeyboardHook = 0; 95 } 96 97 if (!(retKeyboard)) throw new Exception("卸載鉤子失敗!"); 98 } 99 //ToAscii職能的轉換指定的虛擬鍵碼和鍵盤狀態的相應字符或字符 100 [DllImport("user32")] 101 public static extern int ToAscii(int uVirtKey, //[in] 指定虛擬關鍵代碼進行翻譯。 102 int uScanCode, // [in] 指定的硬件掃描碼的關鍵須翻譯成英文。高階位的這個值設定的關鍵,如果是(不壓) 103 byte[] lpbKeyState, // [in] 指針,以256字節數組,包含當前鍵盤的狀態。每個元素(字節)的數組包含狀態的一個關鍵。如果高階位的字節是一套,關鍵是下跌(按下)。在低比特,如果設置表明,關鍵是對切換。在此功能,只有肘位的CAPS LOCK鍵是相關的。在切換狀態的NUM個鎖和滾動鎖定鍵被忽略。 104 byte[] lpwTransKey, // [out] 指針的緩沖區收到翻譯字符或字符。 105 int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. 106 107 //獲取按鍵的狀態 108 [DllImport("user32")] 109 public static extern int GetKeyboardState(byte[] pbKeyState); 110 111 112 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 113 private static extern short GetKeyState(int vKey); 114 115 private const int WM_KEYDOWN = 0x100;//KEYDOWN 116 private const int WM_KEYUP = 0x101;//KEYUP 117 private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN 118 private const int WM_SYSKEYUP = 0x105;//SYSKEYUP 119 120 private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) 121 { 122 // 偵聽鍵盤事件 123 if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null)) 124 { 125 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); 126 // raise KeyDown 127 if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) 128 { 129 Keys keyData = (Keys)MyKeyboardHookStruct.vkCode; 130 System.Windows.Forms.KeyEventArgs e = new System.Windows.Forms.KeyEventArgs(keyData); 131 KeyDownEvent(this, e); 132 } 133 134 //鍵盤按下 135 if (KeyPressEvent != null && wParam == WM_KEYDOWN) 136 { 137 byte[] keyState = new byte[256]; 138 GetKeyboardState(keyState); 139 140 byte[] inBuffer = new byte[2]; 141 if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1) 142 { 143 KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]); 144 KeyPressEvent(this, e); 145 } 146 } 147 148 // 鍵盤抬起 149 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)) 150 { 151 Keys keyData = (Keys)MyKeyboardHookStruct.vkCode; 152 System.Windows.Forms.KeyEventArgs e = new System.Windows.Forms.KeyEventArgs(keyData); 153 KeyUpEvent(this, e); 154 } 155 156 } 157 //如果返回1,則結束消息,這個消息到此為止,不再傳遞。 158 //如果返回0或調用CallNextHookEx函數則消息出了這個鉤子繼續往下傳遞,也就是傳給消息真正的接受者 159 return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); 160 } 161 ~KeyboardHook() 162 { 163 Stop(); 164 } 165 } 166 } View Code三.如何使用上面的工具類?
1 public partial class hook : Window 2 { 3 KeyboardHook k_hook; 4 public hook() 5 { 6 InitializeComponent(); 7 k_hook = new KeyboardHook(); 8 //k_hook.KeyDownEvent += new System.Windows.Forms.KeyEventHandler(hook_KeyDown);//鉤住鍵按下 9 k_hook.KeyPressEvent += K_hook_KeyPressEvent; 10 k_hook.Start();//安裝鍵盤鉤子 11 } 12 13 private void K_hook_KeyPressEvent(object sender, KeyPressEventArgs e) 14 { 15 //tb1.Text += e.KeyChar; 16 int i = (int)e.KeyChar; 17 System.Windows.Forms.MessageBox.Show(i.ToString()); 18 } 19 20 private void hook_KeyDown(object sender, KeyEventArgs e) 21 { 22 tb1.Text += (char)e.KeyData; 23 24 25 //判斷按下的鍵(Alt + A) 26 //if (e.KeyValue == (int)Keys.A && (int)System.Windows.Forms.Control.ModifierKeys == (int)Keys.Alt) 27 //{ 28 // System.Windows.Forms.MessageBox.Show("ddd"); 29 //} 30 } 31 32 private void Window_Unloaded(object sender, RoutedEventArgs e) 33 { 34 k_hook.Stop(); 35 } 36 } View Code//注意幾種不同的鍵值判斷:?
//1>.單普通鍵(例如A)?
//2>.單控制鍵+單普通鍵(例如Ctrl+A)?
//3>.多控制鍵+單普通鍵(例如Ctrl+Alt+A)?
//上面的代碼中演示了2,其它情況以此類推,無非就是添幾個條件再&&起來就好
四.使用全局鍵盤監聽需要注意的問題(請讀者朋友務必看看)
1.在應用程序中使用全局鍵盤監聽,會被360發現,彈窗提示用戶“有程序正在監聽鍵盤輸入,是否阻止?”
所以如果程序中必須要用Hook應該告訴用戶不會泄露其信息等等
或者直接把App提交給360審核
否則殺軟的提示會對用戶體驗造成極大的影響
轉載于:https://www.cnblogs.com/zhaoyuncai/p/7684958.html
總結
以上是生活随笔為你收集整理的C#全局键盘监听(Hook)的使用(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 函数中{}输出格式详解(C#)
- 下一篇: Django REST Framewor