天天看點

unity中使用c#鈎子

目的:解决在应用程序最小化后无法监听到系统按键事件的情况

解决问题的过程很好笑

我先找到了第一个方法

脚本一:

//使用方法,脚本挂在场景中物体上即可
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
//using System.Windows.Forms;
using UnityEngine;

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


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

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

    //获取程序集模块的句柄
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    //全局钩子键盘为13
    private const int WH_KEYBOARD_LL = 13;

    //按下键
    private const int WM_KEYDOWN = 0x0100;

    private static LowLevelKeyboardProc _proc = HookCallback;

    private static IntPtr _hookID = IntPtr.Zero;

    void Start()
    {
        _hookID = SetHook(_proc);
    }


    void OnApplicationQuit()
    {
        UnhookWindowsHookEx(_hookID);
    }

    //安装Hook,用于截获键盘。
    private static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        //nCode>0表示此消息已由Hook函数处理了,不会交给Windows窗口过程处理了
        //nCode=0则表示消息继续传递给Window消息处理函数
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            //UnityEngine.Debug.Log("Keydown:" + vkCode);            
            switch (vkCode)
            {
                case 65:
                    Debug.Log("A");
                    break;
                case 66:
                    Debug.Log("B");
                    break;
                case 67:
                    Debug.Log("C");
                    break;
                case 68:
                    Debug.Log("D");
                    break;
                case 69:
                    Debug.Log("E");
                    break;
                case 70:
                    Debug.Log("F");
                    break;
                case 71:
                    Debug.Log("G");
                    break;
                case 72:
                    Debug.Log("H");
                    break;
                case 73:
                    Debug.Log("I");
                    break;
                case 74:
                    Debug.Log("J");
                    break;
                case 75:
                    Debug.Log("K");
                    break;
                case 76:
                    Debug.Log("L");
                    break;
                case 77:
                    Debug.Log("M");
                    break;
                case 78:
                    Debug.Log("N");
                    break;
                case 79:
                    Debug.Log("O");
                    break;
                case 80:
                    Debug.Log("P");
                    break;
                case 81:
                    Debug.Log("Q");
                    break;
                case 82:
                    Debug.Log("R");
                    break;
                case 83:
                    Debug.Log("S");
                    break;
                case 84:
                    Debug.Log("T");
                    break;
                case 85:
                    Debug.Log("U");
                    break;
                case 86:
                    Debug.Log("V");
                    break;
                case 87:
                    Debug.Log("W");
                    break;
                case 88:
                    Debug.Log("X");
                    break;
                case 89:
                    Debug.Log("Y");
                    break;
                case 90:
                    Debug.Log("Z");
                    break;
                default:
                    break;
            }
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);//传给下一个钩子
    }
}
           

上方的脚本已经能够满足对键盘上26个按键的监听了。如果不够用,可以选择对照ASCII表进行补充!

特点:最后被按下并一直按住的键会不停输出,这在我所开发的项目中不合适

你也可以参考第二个写法,这里有鼠标相关的事件

脚本二:

//使用方式
	//1.在窗口 新建按钮(开始hook) 点击事件中执行Hook_Start
	//2.在窗口 新建按钮(关闭hook) 点击事件中执行Hook_Clear
	//3.在KeyBoardHookProc 中编写hook到键盘要进行的操作
	//4.在MouseHookProc 中编写hook到鼠标要进行的操作

	//一些依赖项
	[DllImport("user32.dll")]
	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")]
	public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
	
	[DllImport("kernel32.dll")]
	public static extern int GetCurrentThreadId();
	
	[DllImport("kernel32.dll")]
	public static extern IntPtr GetModuleHandle(string name);
	
	[DllImport("User32.dll")]
	public static extern void keybd_event(Byte bVk, Byte bScan, Int32 dwFlags, Int32 dwExtraInfo);

	//鼠标事件映射
	private const int WM_MOUSEMOVE = 0x200;
	private const int WM_LBUTTONDOWN = 0x201;
	private const int WM_RBUTTONDOWN = 0x204;
	private const int WM_MBUTTONDOWN = 0x207;
	private const int WM_LBUTTONUP = 0x202;
	private const int WM_RBUTTONUP = 0x205;
	private const int WM_MBUTTONUP = 0x208;
	private const int WM_LBUTTONDBLCLK = 0x203;
	private const int WM_RBUTTONDBLCLK = 0x206;
	private const int WM_MBUTTONDBLCLK = 0x209;
	
	//鼠标hook
	public const int WH_MOUSE_LL = 14;
	//键盘hook
	public const int WH_KEYBOARD_LL = 13;
	public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
	
	//回调
	HookProc KeyBoardHookProcedure;
	HookProc _mouseHookProcedure;
	
	//hook到的消息结构
	[StructLayout(LayoutKind.Sequential)]
	public class KeyBoardHookStruct
	{
	    public int vkCode;
	    public int scanCode;
	    public int flags;
	    public int time;
	    public int dwExtraInfo;
	}
	[StructLayout(LayoutKind.Sequential)]
     public class MouseHookStruct
     {
         public POINT pt;
         public int hWnd;
         public int wHitTestCode;
         public int dwExtraInfo;
     }
	
	 //开启hook
     public void Hook_Start()
     {
         KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
         hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure,GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
         
         _mouseHookProcedure = new HookProc(MouseHookProc);
         _hMouseHook = SetWindowsHookEx(WH_MOUSE_LL,_mouseHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);
     }

     //关闭hook  
     public void Hook_Clear()
     {
         bool retKeyboard = true;
         bool retMouse = true;
         retKeyboard = UnhookWindowsHookEx(hHook);
         hHook = 0;
         retMouse = UnhookWindowsHookEx(_hMouseHook);
         _hMouseHook = 0;
     }

 	//键盘hook到之后的操作
    private int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));


            if (kbh.vkCode == (int)Keys.D && wParam==256)  //D
            {
               //如果按下了D 要进行的处理
            }
        }
        return CallNextHookEx(hHook, nCode, wParam, lParam);
    }

 	//鼠标hook到之后的操作
    private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
    {
        MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));

        int x = MyMouseHookStruct.pt.x;
        int y = MyMouseHookStruct.pt.y;

        if ( wParam== WM_LBUTTONDOWN)
        {
          //鼠标左键按下要进行的处理
        }  
        return CallNextHookEx(_hMouseHook, nCode, wParam, lParam);
    }
           

方法三:

2000 years ago 我发现了一篇文章,

四个字:妙不可言

非常感谢,终于找到了,这就是我需要的(获取按下事件)

using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

public class GlobalInput : MonoBehaviour
{
    #region SINGLETON
    static GlobalInput _instance;
    public static GlobalInput Instance
    {
        get
        {
            if( _instance == null )
            {
                _instance = GameObject.FindObjectOfType<GlobalInput>();
                
                if( _instance == null )
                {
                    GameObject container = new GameObject("GlobalInputManager");
                    _instance = container.AddComponent<GlobalInput>();
                }
            }
        
            return _instance;
        }
    }
    #endregion

    /* Variables */

    // Hook types
    private enum HookType : int
    {
        WH_JOURNALRECORD = 0,
        WH_JOURNALPLAYBACK = 1,
        WH_KEYBOARD = 2,
        WH_GETMESSAGE = 3,
        WH_CALLWNDPROC = 4,
        WH_CBT = 5,
        WH_SYSMSGFILTER = 6,
        WH_MOUSE = 7,
        WH_HARDWARE = 8,
        WH_DEBUG = 9,
        WH_SHELL = 10,
        WH_FOREGROUNDIDLE = 11,
        WH_CALLWNDPROCRET = 12,
        WH_KEYBOARD_LL = 13,
        WH_MOUSE_LL = 14
    }

    public static bool[] keyStates = new bool[255];
    public static bool[] keyDownStates = new bool[255];
    public static bool[] keyUpStates = new bool[255];

    private const int WM_KEYDOWN     = 0x0100;
    private const int WM_KEYUP       = 0x0101;

    private static IntPtr windowHandle;
    private static LowLevelKeyboardProc llKbProc;
    private static IntPtr hookID = IntPtr.Zero;
    
    // Set the hook
    public static bool SetHook()
    {
        // If no process linked, link hook process
        if( llKbProc == null )
            llKbProc = HookCallback;
            
        // If already hooked, unhook
        if( hookID != IntPtr.Zero )
            UnsetHook();
        
        // Hook new process
        Process curProcess = Process.GetCurrentProcess();
        ProcessModule curModule = curProcess.MainModule;
        windowHandle = GetModuleHandle(curModule.ModuleName);
        hookID = SetWindowsHookEx( (int)HookType.WH_KEYBOARD_LL, llKbProc, windowHandle, 0);

        // If hooking failed, return false
        if( hookID == IntPtr.Zero )
        {
            UnityEngine.Debug.Log( "Failed to hook" );
            return false;
        }
        
        // Hooked successfully
        UnityEngine.Debug.Log( "Hooked successfully" );
        return true;
    }

    // Unhook process
    public static void UnsetHook()
    {
        UnhookWindowsHookEx( hookID );
        hookID = IntPtr.Zero;
    }

    // Hook function for windows to call on keystroke events
    private static IntPtr HookCallback( int nCode, IntPtr wParam, IntPtr lParam )
    {
        if ( nCode >= 0 )
        {
            // Read vkCode from lParam
            int vkCode = Marshal.ReadInt32(lParam);

            // If vkCode key is down, set the key state to true
            if( wParam == (IntPtr)WM_KEYDOWN )
                SetKeyState( vkCode, true );
            
            // If vkCode key is up, set the key state to false
            if( wParam == (IntPtr)WM_KEYUP )
                SetKeyState( vkCode, false );
            
        }

        // Call next hook
        return CallNextHookEx(hookID, nCode, wParam, lParam);
    }

    static void SetKeyState( int key, bool state )
    {
        if( !keyStates[ key] )
            keyDownStates[ key ] = state;
        
        keyStates[ key ] = state;
        keyUpStates[ key ] = !state;
    }

    public static bool GetKey( GlobalKeyCode key )
    {
        return keyStates[ (int)key ];
    }

    public static bool GetKeyDown( GlobalKeyCode key )
    {
        if( keyDownStates[ (int)key ] )
        {
            keyDownStates[ (int)key ] = false;
            return true;
        }
        
        return false;
    }

    public static bool GetKeyUp( GlobalKeyCode key )
    {
        if( keyUpStates[ (int)key ] )
        {
            keyUpStates[ (int)key ] = false;
            return true;
        }
        
        return false;
    }

    static GlobalInput()
    {
        // Hook process
        SetHook();

        GameObject container = new GameObject("GlobalInputManager");
        _instance = container.AddComponent<GlobalInput>();
        DontDestroyOnLoad( _instance );
    }

    void OnDisable()
    {
        UnityEngine.Debug.Log("Application ending after " + Time.time + " seconds");
        UnityEngine.Debug.Log("Uninstall hook");
        UnsetHook();
    }

    /* Extern Functions */
    [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )]
    private static extern IntPtr SetWindowsHookEx( int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId );

    [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )]
    [return: MarshalAs( UnmanagedType.Bool )]
    private static extern bool UnhookWindowsHookEx( IntPtr hhk );

    [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )]
    private static extern IntPtr CallNextHookEx( IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam );

    [DllImport( "kernel32.dll", CharSet = CharSet.Auto, SetLastError = true )]
    private static extern IntPtr GetModuleHandle( string lpModuleName );
    
    [DllImport( "user32.dll" )]
    private static extern IntPtr GetForegroundWindow();

    // LowLevelKeyboardProc template
    private delegate IntPtr LowLevelKeyboardProc( int nCode, IntPtr wParam, IntPtr lParam );
}

public enum GlobalKeyCode : int
{
    LBUTTON  = 0x01,
    RBUTTON  = 0x02,
    CANCEL   = 0x03,
    MBUTTON  = 0x04,
    BACK     = 0x08,
    TAB      = 0x09,
    CLEAR    = 0x0C,
    RETURN   = 0x0D,
    SHIFT    = 0x10,
    CONTROL  = 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,
    LMENU        = 0xA4,
    RMENU        = 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,
}


           
2、键盘事件的调用,直接在Update函数监听,用法和Unity自带的键盘监听相似,具体如下:

 void Update()

{
        if (GlobalInput.GetKeyDown(GlobalKeyCode.S))
        {
            Debug.Log("全局监听键盘按下,检测到键盘当前按下了S");
        }

}