unity采用windows钩子(Hook)实现全局热键监听

HotkeyHook  -> 引入外部方法
using System;
using System.Runtime.InteropServices;

public class HotkeyHook
{
    
    // 安装钩子
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    // 卸载钩子
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool UnhookWindowsHookEx(IntPtr hhk);

    // 传递钩子
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    // 获取模块句柄
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);
    
    [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    public static extern int GetAsyncKeyState(int vKey);
    
    [DllImport("user32.dll")]
    public static extern bool GetKeyboardState(byte[] lpKeyState);
    
    public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    
}
HookType -> 定义钩子类型,方便使用
public enum HookType : int
{
    /// <summary>
    /// 记录输入消息。用于日志或重放功能。
    /// </summary>
    WH_JOURNALRECORD = 0,

    /// <summary>
    /// 重放由 WH_JOURNALRECORD 钩子记录的消息。
    /// </summary>
    WH_JOURNALPLAYBACK = 1,

    /// <summary>
    /// 钩子函数监视键盘消息。
    /// </summary>
    WH_KEYBOARD = 2,

    /// <summary>
    /// 钩子函数监视从消息队列中取出的消息。
    /// </summary>
    WH_GETMESSAGE = 3,

    /// <summary>
    /// 钩子函数监视发送到窗口过程的消息。
    /// </summary>
    WH_CALLWNDPROC = 4,

    /// <summary>
    /// 钩子函数在各种窗口事件之前接收通知。
    /// </summary>
    WH_CBT = 5,

    /// <summary>
    /// 钩子函数用于过滤系统消息。
    /// </summary>
    WH_SYSMSGFILTER = 6,

    /// <summary>
    /// 钩子函数监视鼠标消息。
    /// </summary>
    WH_MOUSE = 7,

    /// <summary>
    /// 钩子函数监视硬件消息。
    /// </summary>
    WH_HARDWARE = 8,

    /// <summary>
    /// 用于调试其他钩子过程。
    /// </summary>
    WH_DEBUG = 9,

    /// <summary>
    /// 钩子函数监视外壳应用程序事件。
    /// </summary>
    WH_SHELL = 10,

    /// <summary>
    /// 钩子函数在前台应用程序空闲时调用。
    /// </summary>
    WH_FOREGROUNDIDLE = 11,

    /// <summary>
    /// 钩子函数在窗口过程处理完消息后接收通知。
    /// </summary>
    WH_CALLWNDPROCRET = 12,

    /// <summary>
    /// 低级别的键盘钩子函数,用于监视全局键盘输入事件。
    /// </summary>
    WH_KEYBOARD_LL = 13,

    /// <summary>
    /// 低级别的鼠标钩子函数,用于监视全局鼠标输入事件。
    /// </summary>
    WH_MOUSE_LL = 14
}
GlobalInput -> 全局输入监听管理
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using UnityEngine;
using Debug = UnityEngine.Debug;

public class GlobalInput : MonoBehaviour
{
    
    #region SINGLETON
    private static GlobalInput _instance;

    public static GlobalInput Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<GlobalInput>();
                if (_instance == null)
                {
                    GameObject container = new GameObject("GlobalInputManager");
                    _instance = container.AddComponent<GlobalInput>();
                }
            }
            return _instance;
        }
    }
    #endregion
    
    private static readonly bool[] KeyStatus = new bool[255];

    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private const int WM_SYSKEYDOWN = 0x0104;
    private const int WM_SYSKEYUP = 0x0105;

    private IntPtr _windowHandle;
    private HotkeyHook.LowLevelKeyboardProc _llKbProc;
    private IntPtr _hookID = IntPtr.Zero;

    private void Awake()
    {
        SetHook();
        _instance = this;
        DontDestroyOnLoad(_instance);
    }
    
    private void SetHook()
    {
        _llKbProc ??= HookCallback;
        Process curProcess = Process.GetCurrentProcess();
        ProcessModule curModule = curProcess.MainModule;
        _windowHandle = HotkeyHook.GetModuleHandle(curModule?.ModuleName);
        _hookID = HotkeyHook.SetWindowsHookEx((int)HookType.WH_KEYBOARD_LL, _llKbProc, _windowHandle, 0);
        if (_hookID == IntPtr.Zero)
        {
            Debug.Log("Failed to hook");
        }
        Debug.Log("Hooked successfully");
    }

    private void UnsetHook()
    {
        HotkeyHook.UnhookWindowsHookEx(_hookID);
        _hookID = IntPtr.Zero;
    }
    
    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
            {
                SetKeyState(vkCode, true);
            }
            if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
            {
                SetKeyState(vkCode, false);
            }
        }
        return HotkeyHook.CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private void SetKeyState(int key, bool state)
    {
        KeyStatus[key] = state;
    }

    /// <summary>
    /// 获取按键状态
    /// </summary>
    /// <param name="key">按键类型</param>
    /// <returns>true:按下 | false:抬起</returns>
    public bool GetKeyStatus(GlobalKeyCode key)
    {
        return KeyStatus[(int)key];
    }

    public bool GetKeyDown(List<GlobalKeyCode> keys)
    {
        return keys.TrueForAll(key => KeyStatus[(int)key]);
    }

    public bool GetKeyUp(List<GlobalKeyCode> keys)
    {
        return keys.TrueForAll(key => !KeyStatus[(int)key]);
    }
    
    void OnDisable()
    {
        UnsetHook();
        Debug.Log("Uninstall hook");
    }
}
其他方式:
判断
if((HotkeyHook.GetAsyncKeyState((int)GlobalKeyCode.LBUTTON) & 0x8000) != 0){
则表示按下鼠标左键
}

判断是否有任意按键按下:

private bool IsAnyKeyDown()
{
    byte[] keyState = new byte[256];
    HotkeyHook.GetKeyboardState(keyState);

    for (int i = 0; i < keyState.Length; i++)
    {
        if ((keyState[i] & 0x80) != 0) // 检查高位字节
        {
            return true;
        }
    }

    return false;
}
GlobalKeyCode -> 按键对应的16进制
public enum GlobalKeyCode : int
{
    LBUTTON  = 0x01,
    RBUTTON  = 0x02,
    CANCEL   = 0x03,
    MBUTTON  = 0x04,
    BACK     = 0x08,
    TAB      = 0x09,
    CLEAR    = 0x0C,
    RETURN   = 0x0D,
    SHIFT    = 0x10,
    CTRL  = 0x11,
    MENU     = 0x12,
    PAUSE    = 0x13,
    CAPITAL  = 0x14,
    ESCAPE   = 0x1B,
    SPACE    = 0x20,
    PRIOR    = 0x21,
    NEXT     = 0x22,
    END      = 0x23,
    HOME     = 0x24,
    LEFT     = 0x25,
    UP       = 0x26,
    RIGHT    = 0x27,
    DOWN     = 0x28,
    SELECT   = 0x29,
    PRINT    = 0x2A,
    EXECUTE  = 0x2B,
    SNAPSHOT = 0x2C,
    INSERT   = 0x2D,
    DELETE   = 0x2E,
    HELP     = 0x2F,

    // Number keys
    N0        = 0x30,
    N1        = 0x31,
    N2        = 0x32,
    N3        = 0x33,
    N4        = 0x34,
    N5        = 0x35,
    N6        = 0x36,
    N7        = 0x37,
    N8        = 0x38,
    N9        = 0x39,
    
    // Letter keys
    A    = 0x41,
    B    = 0x42,
    C    = 0x43,
    D    = 0x44,
    E    = 0x45,
    F    = 0x46,
    G    = 0x47,
    H    = 0x48,
    I    = 0x49,
    J    = 0x4A,
    K    = 0x4B,
    L    = 0x4C,
    M    = 0x4D,
    N    = 0x4E,
    O    = 0x4F,
    P    = 0x50,
    Q    = 0x51,
    R    = 0x52,
    S    = 0x53,
    T    = 0x54,
    U    = 0x55,
    V    = 0x56,
    W    = 0x57,
    X    = 0x58,
    Y    = 0x59,
    Z    = 0x5A,

    LWIN        = 0x5B,
    RWIN        = 0x5C,
    APPS        = 0x5D,
    SLEEP        = 0x5F,

    // Numpad keys
    NUMPAD0        = 0x60,
    NUMPAD1        = 0x61,
    NUMPAD2        = 0x62,
    NUMPAD3        = 0x63,
    NUMPAD4        = 0x64,
    NUMPAD5        = 0x65,
    NUMPAD6        = 0x66,
    NUMPAD7        = 0x67,
    NUMPAD8        = 0x68,
    NUMPAD9        = 0x69,
    MULTIPLY     = 0x6A,
    ADD            = 0x6B,
    SEPARATOR     = 0x6C,
    SUBTRACT     = 0x6D,
    DECIMAL      = 0x6E,
    DIVIDE        = 0x6F,

    // Function keys
    F1        = 0x70,
    F2        = 0x71,
    F3        = 0x72,
    F4        = 0x73,
    F5        = 0x74,
    F6        = 0x75,
    F7        = 0x76,
    F8        = 0x77,
    F9        = 0x78,
    F10        = 0x79,
    F11        = 0x7A,
    F12        = 0x7B,
    F13        = 0x7C,
    F14        = 0x7D,
    F15        = 0x7E,
    F16        = 0x7F,
    F17        = 0x80,
    F18        = 0x81,
    F19        = 0x82,
    F20        = 0x83,
    F21        = 0x84,
    F22        = 0x85,
    F23        = 0x86,
    F24        = 0x87,

    // Lock keys
    NUMLOCK        = 0x90,
    SCROLL        = 0x91,

    // Modifiers
    LSHIFT        = 0xA0,
    RSHIFT        = 0xA1,
    LCONTROL     = 0xA2,
    RCONTROL     = 0xA3,
    LALT        = 0xA4,
    RALT        = 0xA5,

    // Media keys
    BROWSER_BACK         = 0xA6,
    BROWSER_FORWARD     = 0xA7,
    BROWSER_REFRESH     = 0xA8,
    BROWSER_STOP         = 0xA9,
    BROWSER_SEARCH         = 0xAA,
    BROWSER_FAVORITES     = 0xAB,
    BROWSER_HOME        = 0xAC,
    VOLUME_MUTE         = 0xAD,
    VOLUME_DOWN         = 0xAE,
    VOLUME_UP             = 0xAF,
    MEDIA_NEXT_TRACK     = 0xB0,
    MEDIA_PREV_TRACK     = 0xB1,
    MEDIA_STOP             = 0xB2,
    MEDIA_PLAY_PAUSE     = 0xB3,
    LAUNCH_MAIL         = 0xB4,
    LAUNCH_MEDIA_SELECT = 0xB5,
    LAUNCH_APP1         = 0xB6,
    LAUNCH_APP2         = 0xB7,

    // Symbols
    COLON            = 0xBA,
    PLUS            = 0xBB,
    COMMA            = 0xBC,
    HYPHEN            = 0xBD,
    PERIOD            = 0xBE,
    FSLASH            = 0xBF,
    TILDE             = 0xC0,
    BACKQUOTE         = 0xC0, // duplicate of tilde
    LSQUAREBRACKET     = 0xDB,
    BSLASH            = 0xDC,
    PIPE            = 0xDC,    // duplicate of back slash
    RSQUAREBRACKET    = 0xDD,
    QUOTE            = 0xDE,
    PLAY        = 0xFA,
    ZOOM        = 0xFB,
}
用法示例:
private void Update()
{
    if (GlobalInput.Instance.GetKeyDown(new List<GlobalKeyCode>{GlobalKeyCode.LALT,GlobalKeyCode.X}))
    {
        Debug.Log("按下了左Alt + X");
    }
            
}
如果后续需求需要用户可以自己设置快捷键,可直接缓存对应快捷键列表,调用该系统进行判断即可,可根据不同需求进行拓展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值