c# 全局钩子实现扫码枪获取信息。
1.扫描枪获取数据原理基本相当于键盘数据,获取扫描枪扫描出来的数据,一般分为两种实现方式。
a)文本框输入获取焦点,扫描后自动显示在文本框内。
b)使用键盘钩子,勾取扫描枪虚拟按键,根据按键频率进行手动输入和扫描枪扫描判断。
2.要实现系统钩子其实很简单,调用三个Win32的API即可。
SetWindowsHookEx 用于设置钩子。(设立一道卡子,盘查需要的信息)
CallNextHookEx 用于传递钩子(消息是重要的,所以从哪里来,就应该回到哪里去,除非你决定要封锁消息)
UnhookWindowsHookEx 卸载钩子(卸载很重要,卡子设多了会造成拥堵)
版本一:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Runtime.InteropServices;
- using System.Reflection;
- using System.Diagnostics;
- namespace SaomiaoTest2
- {
- /// <summary>
- /// 获取键盘输入或者USB扫描枪数据 可以是没有焦点 应为使用的是全局钩子
- /// USB扫描枪 是模拟键盘按下
- /// 这里主要处理扫描枪的值,手动输入的值不太好处理
- /// </summary>
- public class BardCodeHooK
- {
- public delegate void BardCodeDeletegate(BarCodes barCode);
- public event BardCodeDeletegate BarCodeEvent;
- //定义成静态,这样不会抛出回收异常
- private static HookProc hookproc;
- public struct BarCodes
- {
- public int VirtKey;//虚拟吗
- public int ScanCode;//扫描码
- public string KeyName;//键名
- public uint Ascll;//Ascll
- public char Chr;//字符
- public string BarCode;//条码信息 保存最终的条码
- public bool IsValid;//条码是否有效
- public DateTime Time;//扫描时间,
- }
- private struct EventMsg
- {
- public int message;
- public int paramL;
- public int paramH;
- public int Time;
- public int hwnd;
- }
- [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")]
- private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);
- [DllImport("kernel32.dll")]
- public static extern IntPtr GetModuleHandle(string name);
- delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
- BarCodes barCode = new BarCodes();
- int hKeyboardHook = 0;
- string strBarCode = "";
- private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
- {
- if (nCode == 0)
- {
- EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
- if (wParam == 0x100)//WM_KEYDOWN=0x100
- {
- barCode.VirtKey = msg.message & 0xff;//虚拟吗
- barCode.ScanCode = msg.paramL & 0xff;//扫描码
- StringBuilder strKeyName = new StringBuilder(225);
- if (GetKeyNameText(barCode.ScanCode * 65536, strKeyName, 255) > 0)
- {
- barCode.KeyName = strKeyName.ToString().Trim(new char[] { ' ', '\0' });
- }
- else
- {
- barCode.KeyName = "";
- }
- byte[] kbArray = new byte[256];
- uint uKey = 0;
- GetKeyboardState(kbArray);
- if (ToAscii(barCode.VirtKey, barCode.ScanCode, kbArray, ref uKey, 0))
- {
- barCode.Ascll = uKey;
- barCode.Chr = Convert.ToChar(uKey);
- }
- TimeSpan ts = DateTime.Now.Subtract(barCode.Time);
- if (ts.TotalMilliseconds > 50)
- {//时间戳,大于50 毫秒表示手动输入
- strBarCode = barCode.Chr.ToString();
- }
- else
- {
- if ((msg.message & 0xff) == 13 && strBarCode.Length > 3)
- {//回车
- barCode.BarCode = strBarCode;
- barCode.IsValid = true;
- }
- strBarCode += barCode.Chr.ToString();
- }
- barCode.Time = DateTime.Now;
- if (BarCodeEvent != null)
- BarCodeEvent(barCode);//触发事件
- barCode.IsValid = false;
- }
- }
- return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
- }
- //安装钩子
- 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)
- {
- return UnhookWindowsHookEx(hKeyboardHook);
- }
- return true;
- }
- }
- }
在实践过程中,发现版本一的代码只能扫描条形码,如伴随二维码中的字母出现就不能正确获取数据。
版本二:
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace BarcodeScanner
- {
- 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;
- 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 ScanerHook()
- {
- }
- 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);
- }
- 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.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- {
- _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;
- }
- }
- }
版本二中的代码,实践中发现出现了获取扫描数据却省略“+”加号的情况出现。
因此在版本二中备选处添加
- //判断是+ 强制添加+
- 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;
- }
3.winform在无焦点情况下的使用方式:
- using BarcodeScanner;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- namespace BarCodeTest
- {
- public partial class Form1 : Form
- {
- private ScanerHook listener = new ScanerHook();
- public Form1()
- {
- InitializeComponent();
- listener.ScanerEvent += Listener_ScanerEvent;
- }
- private void Listener_ScanerEvent(ScanerHook.ScanerCodes codes)
- {
- textBox3.Text = codes.Result;
- }
- private void Form1_Load(object sender, EventArgs e)
- {
- listener.Start();
- }
- private void Form1_FormClosed(object sender, FormClosedEventArgs e)
- {
- listener.Stop();
- }
- }
- }
c# 全局钩子实现扫码枪获取信息。的更多相关文章
- (32)forms组件(渲染自建规则:局部钩子函数和全局钩子函数)
要达成渲染自建规则 1.局部钩子函数(某个字段,自定意义规则,不如不能以sb开头,数据库已存在等) 2.全局钩子函数(校验两次密码是否一致) 3.使用css样式 register.html <! ...
- 基于Ajax提交formdata数据、错误信息展示和局部钩子、全局钩子的校验。
formdata重点: 实例化FormData这个类 循环serializeArray可以节省代码量 图片要用$('#id')[0].files[0]来获得 加上contentType:false和p ...
- form(form基础、标签渲染、错误显示 重置信息、form属性、局部钩子、全局钩子)
form基础 Django中的Form使用时一般有两种功能: 1.生成html标签 2.验证输入内容 要想使用django提供的form,要在views里导入form模块 from django im ...
- Django day13 form组件, 渲染错误信息, 全局钩子
一:from组件 二:渲染错误信息 三:全局钩子
- Django12-ModelForm中创建局部钩子和全局钩子
一.局部钩子 命名规则为clean_对象名称,例如上面定义了username.pwd对象,那么可以定义clean_username.clean_pwd的局部钩子进行规则校验 1.例子:定义一个手机号校 ...
- 安全之路 —— 使用Windows全局钩子打造键盘记录器
简介 键盘记录功能一直是木马等恶意软件窥探用户隐私的标配,那么这个功能是怎么实现的呢?在Ring3级下,微软就为我们内置了一个Hook窗口消息的API,也就是SetWindowsHookEx函数,这个 ...
- git自定义项目钩子和全局钩子
钩子介绍 自定义钩子分为:项目钩子和全局钩子 自定义全局钩子: 全局钩子目录结构: (注意:excludes目录结构是我们自定义的目录,规则逻辑在update.d/update.py脚本里实现的,非g ...
- django基础之day09,创建一个forms表单组件进行表单校验,知识点:error_messages,label,required,invalid,局部钩子函数,全局钩子函数, forms_obj.cleaned_data,forms_obj.errors,locals(), {{ forms.label }}:{{ forms }},{{ forms.errors.0 }}
利用forms表单组件进行表单校验,完成用户名,密码,确认密码,邮箱功能的校验 该作业包含了下面的知识点: error_messages,label,required,invalid,局部钩子函数,全 ...
- Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解)
我在之前做了一个关于AJAX和form组件的笔记,可以参考:Django学习笔记(8)——前后台数据交互实战(AJAX):Django学习笔记(6)——Form表单 我觉得自己在写Django笔记(8 ...
随机推荐
- 在linux,windows上安装ruby on rails开发环境
ruby是一个非常优秀的语言,ruby的精髓rails可以让web开发的效率成倍的提高,下面就介绍一下我搭建rails环境的过程.windows下搭建ruby rails web开发环境本篇文章主要是 ...
- 《转》couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145
couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145,有须要的朋友能够參考下. 应为昨天安装的时候没及时 ...
- MySQL的表空间管理
表空间: MySQL没有真正意义上的表空间管理. MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间. 一般来说,当数据量很小的时候建议使用共享表空间的管理方式. ...
- Snmp常用oid
http://blog.csdn.net/youngqj/article/details/7311849 系统参数(1.3.6.1.2.1.1) OID 描述 备注 请求方式 .1.3.6.1.2 ...
- [NPM] Create a node script to replace a complex npm script
In this lesson we will look at pulling out complex npm script logic into an external JavaScript file ...
- CoreLocation定位
nCoreLocation n简介 n在移动互联网时代,移动app能解决用户的很多生活琐事,比如 p导航:去任意陌生的地方 p周边:找餐馆.找酒店.找银行.找电影院 p n在上述应用中,都用到了地 ...
- Opencv 使用Stitcher类图像拼接生成全景图像
Opencv中自带的Stitcher类可以实现全景图像,效果不错.下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到: \opencv\sourc ...
- hadoop 3.x Replication与Availability不一致
看下面的文字前先确保你的Replication值不大于你设置的虚拟机数量 如图,显示的副本数为3,但是实际可用的只有一台机器,查看了下hadoop003,hadoop004两台机器,果然没有存储数据, ...
- KindEditor4.1.10,支持粘贴图片
转载自https://blog.csdn.net/jimmy0021/article/details/73251406 我已经忘记我是不是从这个博主的那里找到的解决kindeditor粘贴图片的方法了 ...
- Swift 中的高阶函数和函数嵌套
高阶函数 在Swift中,函数可做为“一等公民”的存在,也就意味着,我们可以和使用 int 以及 String 一样,将函数当做 参数.值.类型来使用. 其中,将函数当作一个参数和值来使用可见下: t ...