C#全域鍵盤監聽(Hook)的使用

來源:互聯網
上載者:User

標籤:

一.為什麼需要全域鍵盤監聽?

在某些情況下應用程式需要實現快速鍵執行特定功能,例如大家熟知的QQ功能Ctrl+Alt+A快速鍵,只要QQ程式在運行(無論是擁有焦點還是處於後台運行狀態),都可以按下快速鍵使用此功能...

這個時候在程式中添加鍵盤監聽肯定不能滿足需求了,當使用者焦點不在App上時(如最小化,或者使用者在處理其它事物等等)鍵盤監聽就失效了

二.怎樣才能實現全域鍵盤監聽?

這裡需要用到Windows API,源碼如下:(可以作為一個工具類[KeyboardHook.cs]收藏起來)

using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;using System.Windows.Forms;using System.Reflection;     namespace 壁紙管家{    /// <summary>    /// 鍵盤鉤子    /// [以下代碼來自某網友,並非本人原創]    /// </summary>    class KeyboardHook    {        public event KeyEventHandler KeyDownEvent;        public event KeyPressEventHandler KeyPressEvent;        public event KeyEventHandler KeyUpEvent;             public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);        static int hKeyboardHook = 0; //聲明鍵盤鉤子處理的初始值        //值在Microsoft SDK的Winuser.h裡查詢// http://www.bianceng.cn/Programming/csharp/201410/45484.htm        public const int WH_KEYBOARD_LL = 13;   //線程鍵盤鉤子監聽滑鼠訊息設為2,全域鍵盤監聽滑鼠訊息設為13        HookProc KeyboardHookProcedure; //聲明KeyboardHookProcedure作為HookProc類型        //鍵盤結構        [StructLayout(LayoutKind.Sequential)]        public class KeyboardHookStruct        {            public int vkCode;  //定一個虛擬鍵碼。該代碼必須有一個價值的範圍1至254            public int scanCode; // 指定的硬體掃描碼的關鍵            public int flags;  // 鍵標誌            public int time; // 指定的時間戳記的這個訊息            public int dwExtraInfo; // 指定額外資訊相關的資訊        }        //使用此功能,安裝了一個鉤子        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);                  //調用此函數卸載鉤子        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern bool UnhookWindowsHookEx(int idHook);                  //使用此功能,通過資訊鉤子繼續下一個鉤子        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);             // 取得當前線程編號(線程鉤子需要用到)        [DllImport("kernel32.dll")]        static extern int GetCurrentThreadId();             //使用WINDOWS API函數代替擷取當前執行個體的函數,防止鉤子失效        [DllImport("kernel32.dll")]        public static extern IntPtr GetModuleHandle(string name);             public void Start()        {            // 安裝鍵盤鉤子            if (hKeyboardHook == 0)            {                KeyboardHookProcedure = new HookProc(KeyboardHookProc);                hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);                //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);                //************************************                //鍵盤線程鉤子                //SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要監聽的線程idGetCurrentThreadId(),                //鍵盤全域鉤子,需要引用空間(using System.Reflection;)                //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);                //                //關於SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函數將鉤子加入到鉤子鏈表中,說明一下四個參數:                //idHook 鉤子類型,即確定鉤子監聽何種訊息,上面的代碼中設為2,即監聽鍵盤訊息並且是線程鉤子,如果是全域鉤子監聽鍵盤訊息應設為13,                //線程鉤子監聽滑鼠訊息設為7,全域鉤子監聽滑鼠訊息設為14。lpfn 鉤子子程的地址指標。如果dwThreadId參數為0 或是一個由別的進程建立的                //線程的標識,lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可以指向當前進程的一段鉤子子程代碼。鉤子函數的入口地址,當鉤子鉤到任何                //訊息後便調用這個函數。hInstance應用程式執行個體的控制代碼。標識包含lpfn所指的子程的DLL。如果threadId 標識當前進程建立的一個線程,而且子                //程代碼位於當前進程,hInstance必須為NULL。可以很簡單的設定其為本應用程式的執行個體控制代碼。threaded 與安裝的鉤子子程相關聯的線程的標識符                //如果為0,鉤子子程與所有的線程關聯,即為全域鉤子                //************************************                //如果SetWindowsHookEx失敗                if (hKeyboardHook == 0)                {                    Stop();                    throw new Exception("安裝鍵盤鉤子失敗");                }            }        }        public void Stop()        {            bool retKeyboard = true;                      if (hKeyboardHook != 0)            {                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);                hKeyboardHook = 0;            }                 if (!(retKeyboard)) throw new Exception("卸載鉤子失敗!");        }        //ToAscii職能的轉換指定的虛擬鍵碼和鍵盤狀態的相應字元或字元        [DllImport("user32")]        public static extern int ToAscii(int uVirtKey, //[in] 指定虛擬關鍵代碼進行翻譯。                                         int uScanCode, // [in] 指定的硬體掃描碼的關鍵須翻譯成英文。高階位的這個值設定的關鍵,如果是(不壓)                                         byte[] lpbKeyState, // [in] 指標,以256位元組數組,包含當前鍵盤的狀態。每個元素(位元組)的數組包含狀態的一個關鍵。如果高階位的位元組是一套,關鍵是下跌(按下)。在低位元,如果設定表明,關鍵是對切換。在此功能,只有肘位的CAPS LOCK鍵是相關的。在切換狀態的NUM個鎖和滾動鎖定鍵被忽略。                                         byte[] lpwTransKey, // [out] 指標的緩衝區收到翻譯字元或字元。                                         int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.             //擷取按鍵的狀態        [DllImport("user32")]        public static extern int GetKeyboardState(byte[] pbKeyState);                  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]        private static extern short GetKeyState(int vKey);             private const int WM_KEYDOWN = 0x100;//KEYDOWN        private const int WM_KEYUP = 0x101;//KEYUP        private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN        private const int WM_SYSKEYUP = 0x105;//SYSKEYUP             private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)        {            // 偵聽鍵盤事件            if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))            {                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));                // raise KeyDown                if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))                {                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;                    KeyEventArgs e = new KeyEventArgs(keyData);                    KeyDownEvent(this, e);                }                     //鍵盤按下                if (KeyPressEvent != null && wParam == WM_KEYDOWN)                {                    byte[] keyState = new byte[256];                    GetKeyboardState(keyState);                         byte[] inBuffer = new byte[2];                    if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)                    {                        KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);                        KeyPressEvent(this, e);                    }                }                     // 鍵盤抬起                if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))                {                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;                    KeyEventArgs e = new KeyEventArgs(keyData);                    KeyUpEvent(this, e);                }                 }            //如果返回1,則結束訊息,這個訊息到此為止,不再傳遞。            //如果返回0或調用CallNextHookEx函數則訊息出了這個鉤子繼續往下傳遞,也就是傳給訊息真正的接受者            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);        }        ~KeyboardHook()        {            Stop();        }    }}

三.如何使用上面的工具類?

//0.準備工作//把上面的工具類添加到項目中     //1.首先匯入需要的命名空間using System.Runtime.InteropServices;   //調用WINDOWS API函數時要用到using Microsoft.Win32;  //寫入註冊表時要用到     //2.安裝Hook,在程式入口中寫上下面的代碼(本例中用了WinForm,在Form的構造方法中安裝Hook即可)//安裝鍵盤鉤子k_hook = new KeyboardHook();k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//鉤住鍵按下k_hook.Start();//安裝鍵盤鉤子     //3.判斷輸入索引值(實現KeyDown事件)private void hook_KeyDown(object sender, KeyEventArgs e)        {            //判斷按下的鍵(Alt + A)            if (e.KeyValue == (int)Keys.A && (int)Control.ModifierKeys == (int)Keys.Alt)            {                System.Windows.Forms.MessageBox.Show("按下了指定快速鍵組合");            }}//注意幾種不同的索引值判斷://1>.單普通鍵(例如A)//2>.單修飾鍵+單普通鍵(例如Ctrl+A)//3>.多修飾鍵+單普通鍵(例如Ctrl+Alt+A)//上面的代碼中示範了2,其它情況以此類推,無非就是添幾個條件再&&起來就好     3.搞定了,開始測試

四.使用全域鍵盤監聽需要注意的問題(請讀者朋友務必看看)

1.在應用程式中使用全域鍵盤監聽,會被360發現,彈窗提示使用者“有程式正在監聽鍵盤輸入,是否阻止?”

所以如果程式中必須要用Hook應該告訴使用者不會泄露其資訊等等

或者直接把App提交給360審核

否則殺軟的提示會對使用者體驗造成極大的影響

P.S.本機只在360安全衛士環境下做了測試,其它殺軟未知,應該也會提示類似的危險資訊,故謹慎使用Hook

 

【轉】http://www.bianceng.cn/Programming/csharp/201410/45484.htm

C#全域鍵盤監聽(Hook)的使用

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.