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");
}
}
方法三原文:https://blog.csdn.net/qq_23443731/article/details/99935002