whitefox 發表於 2023-10-15 00:35:50

[C#] (HOOK 鉤子)監聽系統鍵盤消息

當焦點不在程式上時,用程式加入監控按下鍵盤事件是無法捕抓到相關鍵盤消息!
這時就要用HOOK監聽系統整體的消息發送
先創一個專門監聽功能的類別,這裡命名空間 SystemHookusing System;using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Reflection;

namespace SystemHook
{
    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;
        public const int WH_KEYBOARD_LL = 13;
        HookProc KeyboardHookProcedure;

        //鍵盤鉤子結構
        
        public class KeyboardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        // 裝載鉤子
        
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        // 卸載鉤子
        
        public static extern bool UnhookWindowsHookEx(int idHook);

        // 呼叫下一個鉤子
        
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        // 取得當前執行緒編號
        
        static extern int GetCurrentThreadId();

        // 使用WINDOWS API函數取代取得目前執行個體的函數,防止鉤子失效
        
        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);

                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 轉換指定的虛擬鍵碼和鍵盤狀態的對應字元或字元
        
        public static extern int ToAscii(int uVirtKey,
                                         int uScanCode,
                                         byte[] lpbKeyState,
                                         byte[] lpwTransKey,
                                         int fuState);

        // 取得按鍵的狀態
        
        public static extern int GetKeyboardState(byte[] pbKeyState);

        
        private static extern short GetKeyState(int vKey);

        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
        private const int WM_SYSKEYDOWN = 0x104;
        private const int WM_SYSKEYUP = 0x105;

        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));

                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;
                    GetKeyboardState(keyState);

                    byte[] inBuffer = new byte;
                    if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
                    {
                        KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer);
                        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();
        }
    }
}實作上把就是訂閱KeyboardHook類別的KeyDownEvent事件再回調自己設定的事件處理器函數
這裡舉例一個按下Alt+A就彈出訊息提示using System.Runtime.InteropServices;
using Microsoft.Win32;

KeyboardHook kb_hook = new KeyboardHook();
kb_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//鉤住鍵按下
kb_hook.Start();

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]
查看完整版本: [C#] (HOOK 鉤子)監聽系統鍵盤消息