Open landv opened 4 weeks ago
github.com/landv/HookUsbInput
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; using static HookUsbInput.NativeMethods; using static System.Net.Mime.MediaTypeNames; // edit控件内容获取,也就是手动输入内容获取。 namespace HookUsbInput { public class NativeMethods { // 定义常量 public const int WM_GETTEXT = 0x000D; public const int WM_GETTEXTLENGTH = 0x000E; public const int WH_KEYBOARD_LL = 13; public const int WM_KEYDOWN = 0x0100; public const int VK_RETURN = 0x0D; public const int WM_SETTEXT = 0x000C; [DllImport("user32.dll")] public static extern bool EnableWindow(IntPtr hWnd, bool bEnable); // 导入 SendMessage 函数 [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); // 定义委托 public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); // 导入 Windows API 函数 [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr hWnd, EnumChildProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, StringBuilder lParam); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); // 定义枚举窗口的委托 public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); public delegate bool EnumChildProc(IntPtr hWnd, IntPtr lParam); public enum WindowStyles : uint { WS_OVERLAPPED = 0x00000000, WS_POPUP = 0x80000000, WS_CHILD = 0x40000000, WS_MINIMIZE = 0x20000000, WS_VISIBLE = 0x10000000, WS_DISABLED = 0x08000000, WS_CLIPSIBLINGS = 0x04000000, WS_CLIPCHILDREN = 0x02000000, WS_MAXIMIZE = 0x01000000, WS_CAPTION = 0x00C00000, // WS_BORDER | WS_DLGFRAME WS_BORDER = 0x00800000, WS_DLGFRAME = 0x00400000, WS_VSCROLL = 0x00200000, WS_HSCROLL = 0x00100000, WS_SYSMENU = 0x00080000, WS_THICKFRAME = 0x00040000, WS_GROUP = 0x00020000, WS_TABSTOP = 0x00010000, WS_MINIMIZEBOX = 0x00020000, WS_MAXIMIZEBOX = 0x00010000, WS_POPUPWINDOW = WS_OVERLAPPED | WS_POPUP | WS_BORDER | WS_SYSMENU, WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX } public static readonly int GWL_STYLE = -16; // 获取或设置窗口样式用的参数 public static readonly int GWL_EXSTYLE = -20; // 获取或设置扩展窗口样式用的参数 [DllImport("user32.dll", SetLastError = true)] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowLong(IntPtr hWnd, int nIndex); } public class EditInputHook { // 用于禁止和恢复编辑框的句柄 public static IntPtr editWndxx = IntPtr.Zero; private static System.Threading.Timer timer; private static string processName; private static string topLevelClassName; private static string childClassName; private static string lastText = ""; private static IntPtr hookId = IntPtr.Zero; private static NativeMethods.LowLevelKeyboardProc hookCallback; // 自定义委托和事件 public delegate void ContentChangedEventHandler(string content); public static event ContentChangedEventHandler ContentChanged; // 开始监控 public static void StartMonitoring(string processName, string topLevelClassName, string childClassName) { EditInputHook.processName = processName; EditInputHook.topLevelClassName = topLevelClassName; EditInputHook.childClassName = childClassName; timer = new System.Threading.Timer(TimerCallback, null, 0, 100); hookCallback = KeyboardHookCallback; using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { hookId = NativeMethods.SetWindowsHookEx(NativeMethods.WH_KEYBOARD_LL, hookCallback, NativeMethods.GetModuleHandle(curModule.ModuleName), 0); } } // 定时器回调函数 private static void TimerCallback(object state) { Process[] processes = Process.GetProcessesByName(processName); if (processes.Length > 0) { uint processId = (uint)processes[0].Id; NativeMethods.EnumWindows(new NativeMethods.EnumWindowsProc((hWnd, lParam) => { NativeMethods.GetWindowThreadProcessId(hWnd, out uint windowProcessId); if (windowProcessId == processId) { NativeMethods.EnumChildWindows(hWnd, new NativeMethods.EnumChildProc((childWnd, childLParam) => { StringBuilder className = new StringBuilder(256); NativeMethods.GetClassName(childWnd, className, className.Capacity); if (className.ToString() == topLevelClassName) { NativeMethods.EnumChildWindows(childWnd, new NativeMethods.EnumChildProc((editWnd, editLParam) => { StringBuilder editClassName = new StringBuilder(256); NativeMethods.GetClassName(editWnd, editClassName, editClassName.Capacity); if (editClassName.ToString() == childClassName) { int textLength = NativeMethods.SendMessage(editWnd, NativeMethods.WM_GETTEXTLENGTH, 0, null).ToInt32(); StringBuilder text = new StringBuilder(textLength + 1); NativeMethods.SendMessage(editWnd, NativeMethods.WM_GETTEXT, text.Capacity, text); //lastText = text.ToString(); if (textLength >= 14) { lastText = text.ToString(); editWndxx = editWnd; } } return true; }), IntPtr.Zero); } return true; }), IntPtr.Zero); } return true; }), IntPtr.Zero); } else { //Console.WriteLine($"未找到名为{processName}.exe的进程。"); } } // 内容变更事件处理函数 private static void OnContentChanged(string content) { ContentChanged?.Invoke(content); } // 键盘钩子回调函数 private static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)NativeMethods.WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); if (vkCode == NativeMethods.VK_RETURN) { if (lastText.Length >= 14) { OnContentChanged(lastText); lastText = string.Empty; lastText = ""; ClearEditControl(processName, topLevelClassName, childClassName); return (IntPtr)1; } else { return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam); } } } // 在这里返回下一个钩子的处理结果 return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam); } public static void EnableWindowsEditWndxx(bool a) { if (editWndxx!=IntPtr.Zero) { if (a) { //MessageBox.Show(editWndxx.ToString()); NativeMethods.EnableWindow(editWndxx, true); } else { // 禁用窗口 NativeMethods.EnableWindow(editWndxx, false); //MessageBox.Show("禁用输入"); } } } // 清空编辑控件内容方法 public static void ClearEditControl(string processName, string topLevelClassName, string childClassName) { IntPtr editWndx = IntPtr.Zero; Process[] processes = Process.GetProcessesByName(processName); if (processes.Length > 0) { uint processId = (uint)processes[0].Id; NativeMethods.EnumWindows(new NativeMethods.EnumWindowsProc((hWnd, lParam) => { NativeMethods.GetWindowThreadProcessId(hWnd, out uint windowProcessId); if (windowProcessId == processId) { NativeMethods.EnumChildWindows(hWnd, new NativeMethods.EnumChildProc((childWnd, childLParam) => { StringBuilder className = new StringBuilder(256); NativeMethods.GetClassName(childWnd, className, className.Capacity); if (className.ToString() == topLevelClassName) { NativeMethods.EnumChildWindows(childWnd, new NativeMethods.EnumChildProc((editWnd, editLParam) => { StringBuilder editClassName = new StringBuilder(256); NativeMethods.GetClassName(editWnd, editClassName, editClassName.Capacity); if (editClassName.ToString() == childClassName) { int textLength = NativeMethods.SendMessage(editWnd, NativeMethods.WM_GETTEXTLENGTH, 0, null).ToInt32(); StringBuilder text = new StringBuilder(textLength + 1); NativeMethods.SendMessage(editWnd, NativeMethods.WM_GETTEXT, text.Capacity, text); //lastText = text.ToString(); if (textLength >= 14) { editWndx = editWnd; //NativeMethods.ShowWindow(editWnd, 0); //Thread.Sleep(200); //NativeMethods.ShowWindow(editWnd, 5); return true; } } return true; }), IntPtr.Zero); } return true; }), IntPtr.Zero); } return true; }), IntPtr.Zero); } // 清空编辑控件内容 if (editWndx != IntPtr.Zero) { NativeMethods.SendMessage(editWndx, NativeMethods.WM_SETTEXT, 0, new StringBuilder("")); //Thread.Sleep(500); //NativeMethods.ShowWindow(editWndx, 5); } } } }
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace HookUsbInput { public class InputHook { public delegate void ScanerDelegate(ScanerCodes codes); public event ScanerDelegate ScanerEvent; //private const int WM_KEYDOWN = 0x100;//KEYDOWN //private const int WM_KEYUP = 0x101;//KEYUP //private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN //private const int WM_SYSKEYUP = 0x105;//SYSKEYUP //private static int HookProc(int nCode, Int32 wParam, IntPtr lParam); private int hKeyboardHook = 0;//声明键盘钩子处理的初始值 private ScanerCodes codes = new ScanerCodes();//13为键盘钩子 //定义成静态,这样不会抛出回收异常 private static HookProc hookproc; delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //设置钩子 private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //卸载钩子 private static extern bool UnhookWindowsHookEx(int idHook); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //继续下个钩子 private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32", EntryPoint = "GetKeyNameText")] private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize); [DllImport("user32", EntryPoint = "GetKeyboardState")] //获取按键的状态 private static extern int GetKeyboardState(byte[] pbKeyState); [DllImport("user32", EntryPoint = "ToAscii")] //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符 private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags); //int VirtualKey //[in] 指定虚拟关键代码进行翻译。 //int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压) //byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如/果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。 //byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。 //uint fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. [DllImport("kernel32.dll")] //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效 public static extern IntPtr GetModuleHandle(string name); //public InputHook() //{ //} public bool Start() { if (hKeyboardHook == 0) { hookproc = new HookProc(KeyboardHookProc); //GetModuleHandle 函数 替代 Marshal.GetHINSTANCE //防止在 framework4.0中 注册钩子不成功 IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); //WH_KEYBOARD_LL=13 //全局钩子 WH_KEYBOARD_LL // hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0); } return (hKeyboardHook != 0); } public bool Stop() { if (hKeyboardHook != 0) { bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook); hKeyboardHook = 0; return retKeyboard; } return true; } private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) { EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg)); codes.Add(msg); if (ScanerEvent != null && msg.message == 13 && msg.paramH > 0 && !string.IsNullOrEmpty(codes.Result)) { if (codes.Result.Length>=14) { ScanerEvent(codes); } else { return 1; } } return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } public class ScanerCodes { // 这里设置毫秒数,打字快的会受到影响(打字快并且按了回车键的时候是) private int ts = 100; // 指定输入间隔为毫秒以内时为连续输入 private List<List<EventMsg>> _keys = new List<List<EventMsg>>(); private List<int> _keydown = new List<int>(); // 保存组合键状态 private List<string> _result = new List<string>(); // 返回结果集 private DateTime _last = DateTime.Now; private byte[] _state = new byte[256]; private string _key = string.Empty; private string _cur = string.Empty; public EventMsg Event { get { if (_keys.Count == 0) { return new EventMsg(); } else { return _keys[_keys.Count - 1][_keys[_keys.Count - 1].Count - 1]; } } } public List<int> KeyDowns { get { return _keydown; } } public DateTime LastInput { get { return _last; } } public byte[] KeyboardState { get { return _state; } } public int KeyDownCount { get { return _keydown.Count; } } public string Result { get { if (_result.Count > 0) { return _result[_result.Count - 1].Trim(); } else { return null; } } } public string CurrentKey { get { return _key; } } public string CurrentChar { get { return _cur; } } public bool isShift { get { return _keydown.Contains(160); } } public void Add(EventMsg msg) { #region 记录按键信息 // 首次按下按键 if (_keys.Count == 0) { _keys.Add(new List<EventMsg>()); _keys[0].Add(msg); _result.Add(string.Empty); } // 未释放其他按键时按下按键 else if (_keydown.Count > 0) { _keys[_keys.Count - 1].Add(msg); } // 单位时间内按下按键 else if (((TimeSpan)(DateTime.Now - _last)).TotalMilliseconds < ts) { _keys[_keys.Count - 1].Add(msg); } // 从新记录输入内容 else { _keys.Add(new List<EventMsg>()); _keys[_keys.Count - 1].Add(msg); _result.Add(string.Empty); } #endregion _last = DateTime.Now; #region 获取键盘状态 // 记录正在按下的按键 if (msg.paramH == 0 && !_keydown.Contains(msg.message)) { _keydown.Add(msg.message); } // 清除已松开的按键 if (msg.paramH > 0 && _keydown.Contains(msg.message)) { _keydown.Remove(msg.message); } #endregion #region 计算按键信息 int v = msg.message & 0xff; int c = msg.paramL & 0xff; StringBuilder strKeyName = new StringBuilder(500); if (GetKeyNameText(c * 65536, strKeyName, 255) > 0) { _key = strKeyName.ToString().Trim(new char[] { ' ', '\0' }); GetKeyboardState(_state); if (_key.Length == 1 && msg.paramH == 0)// && msg.paramH == 0 { // 根据键盘状态和shift缓存判断输出字符 _cur = ShiftChar(_key, isShift, _state).ToString(); _result[_result.Count - 1] += _cur; } //判断是+ 强制添加+ else if (_key.Length == 5 && msg.paramH == 0 && msg.paramL == 78 && msg.message == 107)// && msg.paramH == 0 { // 根据键盘状态和shift缓存判断输出字符 _cur = Convert.ToChar('+').ToString(); _result[_result.Count - 1] += _cur; } // 备选 else { _cur = string.Empty; } } #endregion } private char ShiftChar(string k, bool isShiftDown, byte[] state) { bool capslock = state[0x14] == 1; bool numlock = state[0x90] == 1; bool scrolllock = state[0x91] == 1; bool shiftdown = state[0xa0] == 1; char chr = (capslock ? k.ToUpper() : k.ToLower()).ToCharArray()[0]; if (isShiftDown) { if (chr >= 'a' && chr <= 'z') { chr = (char)((int)chr - 32); } else if (chr >= 'A' && chr <= 'Z') { if (chr == 'Z') { string s = ""; } chr = (char)((int)chr + 32); } else { string s = "`1234567890-=[];',./"; string u = "~!@#$%^&*()_+{}:\"<>?"; if (s.IndexOf(chr) >= 0) { return (u.ToCharArray())[s.IndexOf(chr)]; } } } return chr; } } public struct EventMsg { public int message; public int paramL; public int paramH; public int Time; public int hwnd; } } }
github.com/landv/HookUsbInput
EditInputHook.cs
inputHook.cs