1. 程式人生 > >鍵盤鉤子 HOOK

鍵盤鉤子 HOOK

請不多說,直接上程式碼。

主要問題是自己解決了  Ctrl + Alt + A ,這樣的三個鍵組合的情況。


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Reflection;

namespace WpfApplication
{
    /// <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裡查詢
        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
        /// <summary>
        /// 鉤子捕獲訊息後,對訊息進行處理
        /// </summary>
        /// <param nCode="int">標識,鍵盤是否操作</param> 
        /// <param wParam="int">鍵盤的操作值</param>
        /// <param lParam="IntPtr">指標</param>
        private int KeyboardHookProcDemo(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode > -1 && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
            {
                KeyboardHookStruct keyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));//獲取鉤子的相關資訊

                KeyEventArgs e = new KeyEventArgs((Keys)(keyboardHookStruct.vkCode));//獲取KeyEventArgs事件的相磁資訊
                switch (wParam)
                {
                    case WM_KEYDOWN://鍵盤按下操作
                    case WM_SYSKEYDOWN:
                        if (KeyDownEvent != null)//如果載入了當前事件
                        {
                            KeyDownEvent(this, e);//呼叫該事件
                        }
                        break;
                    case WM_KEYUP://鍵盤松開操作
                    case WM_SYSKEYUP:
                        if (KeyUpEvent != null)//如果載入了當前事件
                        {
                            KeyUpEvent(this, e);//呼叫該事件                           
                        }
                        break;
                }
                if (keyboardHookStruct.vkCode == (int)Keys.PrintScreen)
                    return 1;
            }
            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
            //return 0;//是否遮蔽當前熱鍵,1為遮蔽,2為執行
        }
        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            int keyShow = 0;
            // 偵聽鍵盤事件
            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);
                    keyShow = ChenckKaisen(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);
                    keyShow = ChenckKaisen(e);
                }
            }
            //如果返回1,則結束訊息,這個訊息到此為止,不再傳遞。
            //如果返回0或呼叫CallNextHookEx函式則訊息出了這個鉤子繼續往下傳遞,也就是傳給訊息真正的接受者
            return keyShow > 0 ? 1 : CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);

        }

        /// <summary>
        /// 禁止指定組合鍵
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private static int ChenckKaisen(KeyEventArgs e)
        {
            int kaisen = 0;
            if (e.KeyValue == (int)Keys.A && (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt)
            {
                kaisen = 3;
            }
            else if (e.KeyValue == (int)Keys.A
               && ((int)Control.ModifierKeys == (int)Keys.Alt
                || (int)Control.ModifierKeys == (int)Keys.Control))
            {
                kaisen = 2;
            }
            else if (e.KeyValue == (int)Keys.S
                && ((int)Control.ModifierKeys == (int)Keys.Alt
            || (int)Control.ModifierKeys == (int)Keys.Control))
            {
                kaisen = 2;
            }
            else if (e.KeyValue == (int)Keys.P && (int)Control.ModifierKeys == (int)Keys.Control)
            {
                kaisen = 2;
            }
            else if (e.KeyValue == (int)Keys.PrintScreen)
            {
                kaisen = 1;
            }

            if (kaisen > 0)
            {
                //在剪貼簿放一張自定義圖片
            }
            return kaisen;
        }

        ~KeyboardHook()
        {
            Stop();
        }
    }
}


下面是WinFom中呼叫示例:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;   //呼叫WINDOWS API函式時要用到
using Microsoft.Win32;
using WpfApplication;  //寫入登錄檔時要用到

namespace WindowsFormsApplication
{
    public partial class Hook : Form
    {
        internal KeyboardHook k_hook { get; set; }
        public Hook()
        {
            //2.安裝Hook,在程式入口中寫上下面的程式碼(本例中用了WinForm,在Form的構造方法中安裝Hook即可)
            //安裝鍵盤鉤子
            k_hook = new KeyboardHook();
            k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//鉤住鍵按下


            InitializeComponent();
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            k_hook.Stop();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            k_hook.Stop();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            k_hook.Start();//安裝鍵盤鉤子
        }

        //3.判斷輸入鍵值(實現KeyDown事件)
        private void hook_KeyDown(object sender, KeyEventArgs e)
        {
            //if (e.KeyValue == (int)Keys.A && (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt)
            //{
            //    System.Windows.Forms.MessageBox.Show("按下了指定快捷鍵組合 ctrl + alt + a");
            //}
        }
    }
}