C#全局鈎子和局部鈎子記錄


源自:https://blog.csdn.net/programvae/article/details/80292076

最近碰巧要使用鍵盤鈎子,於是在網上搜索了一番,發現大多數博客的文章都是雷同的,根本就沒有講清楚全局鈎子和局部鈎子的區別,於是特開一貼,講全局鈎子和局部鈎子捋一捋。也供后面的人學習。
   因為大部分應用都應該采用局部鈎子,所以我這兒使用的是局部鈎子,而全局鈎子的例子網上到處都是。
   大部分網上參考文章都只是展示了全局鈎子的寫法,而線程鈎子的寫法和介紹相對少一些,特別是關鍵語句上如果定義的不正確是沒有任何效果的,在自己反復嘗試后決定留下一個正確的版本分享出來
  
  代碼如下

 

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

namespace AssistToolSet.Util
{
    /// <summary>
    /// 鍵盤鈎子類
    /// </summary>
    public class Hook
    {
        public delegate int HookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam);
        public enum WH_CODE : int
        {
            WH_JOURNALRECORD = 0,
            WH_JOURNALPLAYBACK = 1,
            /// <summary>
            /// 進程鈎子
            /// </summary>
            WH_KEYBOARD = 2,

            /// <summary>
            /// 底層鍵盤鈎子 全局鈎子就是用這個

            /// </summary>
            WH_KEYBOARD_LL = 13,
        }

        public enum HC_CODE : int
        {
            HC_ACTION = 0,
            HC_GETNEXT = 1,
            HC_SKIP = 2,
            HC_NOREMOVE = 3,
            HC_NOREM = 3,
            HC_SYSMODALON = 4,
            HC_SYSMODALOFF = 5
        }

        /// <summary>
        /// 安裝鈎子
        /// </summary>
        [DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr SetWindowsHookEx(WH_CODE idHook, HookProc lpfn, IntPtr pInstance, uint threadId);

        /// <summary>
        /// 卸載鈎子
        /// </summary>
        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
        /// <summary>
        /// 傳遞鈎子
        /// </summary>
        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(IntPtr pHookHandle, WH_CODE nCodem, Int32 wParam, IntPtr lParam);

        /// <summary>
        /// 獲取全部按鍵狀態
        /// </summary>
        /// <param name="pbKeyState"></param>
        /// <returns>非0表示成功</returns>
        [DllImport("user32.dll")]
        public static extern int GetKeyboardState(byte[] pbKeyState);

        /// <summary>
        /// 獲取程序集模塊的句柄
        /// </summary>
        /// <param name="lpModuleName"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        /// <summary>
        /// 獲取當前進程中的當前線程ID
        /// </summary>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern uint GetCurrentThreadId();

        #region 私有變量

        private byte[] mKeyState = new byte[256];
        private Keys mKeyData = Keys.None; //專門用於判斷按鍵的狀態

        /// <summary>
        /// 鍵盤鈎子句柄
        /// </summary>
        private IntPtr mKetboardHook = IntPtr.Zero;

        /// <summary>
        /// 鍵盤鈎子委托實例
        /// </summary>
        private HookProc mKeyboardHookProcedure;

        #endregion

        #region 鍵盤事件

        public event KeyEventHandler OnKeyDown;
        public event KeyEventHandler OnKeyUp;

        #endregion

        /// <summary>
        /// 構造函數
        /// </summary>
        public Hook()
        {
            GetKeyboardState(this.mKeyState);
        }

        ~Hook()
        {
            UnInstallHook();
        }

        /// <summary>
        /// 鍵盤鈎子處理函數
        /// </summary>
        private int KeyboardHookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam)
        {
        /*全局鈎子應該這樣設定
         KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
        */
            // 定義為線程鈎子時,wParam的值是擊打的按鍵,與Keys里的對應按鍵相同
            if ((nCode == (int)HC_CODE.HC_ACTION) && (this.OnKeyDown != null || this.OnKeyUp != null))
            {
                mKeyData = (Keys)wParam;
                KeyEventArgs keyEvent = new KeyEventArgs(mKeyData);
                //這里簡單的通過lParam的值的正負情況與按鍵的狀態相關聯
                if (lParam.ToInt32() > 0 && this.OnKeyDown != null)
                {
                    this.OnKeyDown(this, keyEvent);
                }
                else if (lParam.ToInt32() < 0 && this.OnKeyUp != null)
                {
                    this.OnKeyUp(this, keyEvent);
                }
            }
            if (ShortcutManagement.s_bHotkeyUsed)
            {
                ShortcutManagement.s_bHotkeyUsed = false;
                return 1;
            }

            return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam);
        }
        /// <summary>
        /// 安裝鈎子
        /// </summary>
        /// <returns></returns>
        public bool InstallHook()
        {
            //線程鈎子時一定要通過這個取得的值才是操作系統下真實的線程
            uint result = GetCurrentThreadId();

            if (this.mKetboardHook == IntPtr.Zero)
            {
                this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
                //注冊線程鈎子時第三個參數是空
                this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD, this.mKeyboardHookProcedure, IntPtr.Zero, result);
                /*
                如果是全局鈎子應該這樣使用
                this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD_LL, mKeyboardHookProcedure,GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
                */
                if (this.mKetboardHook == IntPtr.Zero)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 卸載鈎子
        /// </summary>
        /// <returns>true表示成功 </returns>
        public bool UnInstallHook()
        {
            bool result = true;
            if (this.mKetboardHook != IntPtr.Zero)
            {
                result = UnhookWindowsHookEx(this.mKetboardHook) && result;
                this.mKetboardHook = IntPtr.Zero;
            }
            return result;
        }
    }

}
--------------------- 
作者:PGEva 
來源:CSDN 
原文:https://blog.csdn.net/programvae/article/details/80292076 
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
View Code

 

 

通過這次認知,意識到,以后如果要做這些接觸相關的api的時候,我們應該盡量去查官方文檔,而不是一開始就是查看別人的博客。應該以官方文檔為主。一定要記住鍵盤鈎子事件。

32位和64位的系統不一樣。


免責聲明!

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



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