当前位置: 首页 > article >正文

C#通过键盘钩子实现二维扫描枪传输数据的接收

目录


1、使用需求
2、实现思路
3、接入钩子win32接口API
4、声明键盘钩子消息结构
5、安装键盘钩子
6、使用实例
7、注意事项


1、使用需求

最近接收到需求,需要通过扫描二维码将获取到的信息填充到窗体里面。

2、实现思路

通过查看二维扫描枪的说明书可知,扫描枪传输数据可支持标准键盘模拟输入,于是可通过键盘钩子监听事件来实现。

3、接入钩子win32接口API

//定义成静态,这样不会抛出回收异常
private static HookProc hookproc;
delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

//设置钩子(设立一道卡子,盘查需要的信息)
[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);

//ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
[DllImport("user32", EntryPoint = "ToAscii")]
private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);

//使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);

//使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);

4、声明键盘钩子消息结构

public struct EventMsg
{
    public int message;
    public int paramL;
    public int paramH;
    public int Time;
    public int hwnd;
}

5、安装键盘钩子

//第二步:声明,定义委托
public delegate void ScanerDelegate(string codes);
public event ScanerDelegate ScanerEvent;

/// <summary>
/// 声明键盘钩子处理的初始值
/// </summary>
private int hKeyboardHook = 0;
private ScanerCodes codes = new ScanerCodes();//13为键盘钩子

public BarcodeScannerHelper()
{
}

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))
	{
		//ScanerEvent(codes);
		ScanerEvent(codes.Result);
	}
	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
	{
		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.message == 161)
		{
			msg.message -= 1;
			msg.paramH -= 1;
		}
		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;
	}
}

6、使用实例

//创建钩子类
BarcodeScannerHelper scannerListener = new BarcodeScannerHelper();

scannerListener.ScanerEvent += ScannerListener_ScanerEvent;
scannerListener.Start();//开始监听
//监听过程
//scannerListener.Stop();//停止监听

//监听接收事件
private void ScannerListener_ScanerEvent(string codes)
{
    //将接收到的数据进行处理
}

7、注意事项

部分扫描枪在区分大小写时用的是Left Shift,有的为Right Shift,两种处理方式有区别,需要特别注意,如果只处理一种会导致扫描出的字母均为小写,此实例已经两种方式均已经处理

技术交流请加QQ群:996775415


http://www.kler.cn/a/315959.html

相关文章:

  • 嵌入式硬件实战基础篇(一)-STM32+DAC0832 可调信号发生器-产生方波-三角波-正弦波
  • 试编写算法将单链表就地逆置(默认是带头节 点,如果是不带头节点地逆置呢?)
  • 计算机网络 (1)互联网的组成
  • websocket初始化
  • 什么时候需要复写hashcode()和compartTo方法
  • docker镜像源,亲测可用,时间2024-11-14
  • 亮相世界制造业大会,智象未来(HiDream.ai)揭示产业发展新趋势
  • python爬虫:将知乎专栏文章转为pdf
  • JMeter(需要补充请在留言区发给我,谢谢)
  • Mysql梳理7——分页查询
  • 经验笔记:Python 脚本打包为可执行文件(.exe)
  • 「Java开发指南」如何用MyEclipse搭建Adobe和Spring Flex?(二)
  • 自由切换存储方式:本地 or OSS
  • 《深度学习》卷积神经网络CNN 原理及其流程解析
  • 网络安全中GET和POST区别在哪?
  • 【原创】java+springboot+mysql党员教育网系统设计与实现
  • 利用WPF绘制轮廓并保存为图片
  • Logstash 安装与部署(无坑版)
  • Vue:加载本地视频
  • 【Opencv知识】图像梯度如何理解?
  • 图文深入理解SQL语句的执行过程
  • Android下反调试与反反调试
  • 开源 AI 智能名片链动 2+1 模式 S2B2C 商城小程序与社交电商的崛起
  • 网络管理:防火墙和安全组配置详解
  • JVM 调优篇7 调优案例4- 线程溢出
  • 【Git】深入理解 Git 版本回退:方法与实践