当前位置:网站首页>c# 键盘钩子
c# 键盘钩子
2022-06-29 10:44:00 【全栈程序员站长】
大家好,又见面了,我是你们的朋友全栈君。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace KeyboardHookPro
{
public class ScanerHook
{
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;
private delegate int HookProc(int nCode, int 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, int 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 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, int 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))
//{
ScanerEvent(codes);
//}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
public class ScanerCodes
{
private int ts = 100; // 指定输入间隔为300毫秒以内时为连续输入
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 => _keydown;
public DateTime LastInput => _last;
public byte[] KeyboardState => _state;
public int KeyDownCount => _keydown.Count;
public string Result
{
get
{
if (_result.Count > 0)
{
return _result[_result.Count - 1].Trim();
}
else
{
return null;
}
}
}
public string CurrentKey => _key;
public string CurrentChar => _cur;
public bool isShift => _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 ((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)(chr - 32);
}
else if (chr >= 'A' && chr <= 'Z')
{
if (chr == 'Z')
{
string s = "";
}
chr = (char)(chr + 32);
}
else
{
string s = "`1234567890-=[];',./";
string u = "[email protected]#$%^&*()_+{}:\"<>?";
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;
}
}
}用法
private ScanerHook listener = new ScanerHook();
public InitHook()
{
listener.Start();
listener.ScanerEvent += Listener_ScanerEvent;//执行函数
this.FormClosed += (sender, e) =>
{
listener.Stop();
};
}
private void Listener_ScanerEvent(ScanerHook.ScanerCodes codes)
{
string msg = codes.Result;//此处输出
}发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/132342.html原文链接:https://javaforall.cn
边栏推荐
- After 22 years in office, the father of PowerShell will leave Microsoft: he was demoted by Microsoft for developing PowerShell
- Self-Improvement! Junior college "counter attack" master of Zhejiang University, 3 SCI, and finally become a doctor of Tsinghua University!
- Uboot for embedded driver development -- common command parameters in uboot
- TTL serial port learning infrared remote control module can be extended to network control
- 新版CorelDRAW Technical Suite2022最新详细功能介绍
- 软件工程导论——第五章——总体设计
- Limit introduction summary
- MATLAB basic Max to find the maximum value of one-dimensional or two-dimensional array +sleep (pause)
- 适合小白的树莓派opencv4.0安装
- The Chinese Computational Linguistics Conference and the national knowledge atlas and Semantic Computing Conference are in full swing
猜你喜欢

Babbitt | yuancosmos daily must read: HTC announced the launch of the first yuancosmos mobile phone, which costs about 2700 yuan. What are the new ways to play

Opencv4.0 installation of raspberry pie for Xiaobai

【每日3题(3)】重新格式化电话号码

ruoyi框架中添加sharding sphere5.0.0分表(通过spi添加自定义分表策略)

又拍云 Redis 的改进之路

(JS) array de duplication

XML外部实体注入漏洞(一)
![Leetcode 535 encryption and decryption of tinyurl [map] the leetcode road of heroding](/img/76/709bbbbd8eb01f32683a96c4abddb9.png)
Leetcode 535 encryption and decryption of tinyurl [map] the leetcode road of heroding

直击产业落地!飞桨重磅推出业界首个模型选型工具

在日本的 IT 公司工作是怎样一番体验?
随机推荐
Take another picture of cloud redis' improvement path
Multithreaded high concurrency server: three problems
Specific method and example program of Siemens s7-200smart control stepping motor
Uboot for embedded driver development -- common command parameters in uboot
(JS) iterator mode
【高并发】缓存思路
The first "cyborg" in the world died, and he only transformed himself to "change his life against the sky"
[3 questions per day (2)] minimum operand for generating alternate binary strings
Encore une fois, le chemin de l'amélioration de redis Cloud
Qt学习16 Qt 对象间的父子关系
What are the pop, push, unshift, and shift of the (JS) array?
高效远程办公的基石:有效沟通 |社区征文
Course design for the end of the semester: product sales management system based on SSM
rxjs Observable 设计原理背后的 Pull 和 Push 思路
The use of Fibonacci sequence and bubble sort in C language
分布式缓存之Memcached
ETL为什么经常变成ELT甚至LET?
Exclusive interview with head of suss NIFT: the future of Web3 is inseparable from the governance of "everyone for me, I for everyone"
Unity learning notes --vector3 how to set default parameters
Xuetong denies that the theft of QQ number is related to it: it has been reported; IPhone 14 is ready for mass production: four models are launched simultaneously; Simple and elegant software has long