原文: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# 全局钩子实现扫码枪获取信息。的更多相关文章

  1. (32)forms组件(渲染自建规则:局部钩子函数和全局钩子函数)

    要达成渲染自建规则 1.局部钩子函数(某个字段,自定意义规则,不如不能以sb开头,数据库已存在等) 2.全局钩子函数(校验两次密码是否一致) 3.使用css样式 register.html <! ...

  2. 基于Ajax提交formdata数据、错误信息展示和局部钩子、全局钩子的校验。

    formdata重点: 实例化FormData这个类 循环serializeArray可以节省代码量 图片要用$('#id')[0].files[0]来获得 加上contentType:false和p ...

  3. form(form基础、标签渲染、错误显示 重置信息、form属性、局部钩子、全局钩子)

    form基础 Django中的Form使用时一般有两种功能: 1.生成html标签 2.验证输入内容 要想使用django提供的form,要在views里导入form模块 from django im ...

  4. Django day13 form组件, 渲染错误信息, 全局钩子

    一:from组件 二:渲染错误信息 三:全局钩子

  5. Django12-ModelForm中创建局部钩子和全局钩子

    一.局部钩子 命名规则为clean_对象名称,例如上面定义了username.pwd对象,那么可以定义clean_username.clean_pwd的局部钩子进行规则校验 1.例子:定义一个手机号校 ...

  6. 安全之路 —— 使用Windows全局钩子打造键盘记录器

    简介 键盘记录功能一直是木马等恶意软件窥探用户隐私的标配,那么这个功能是怎么实现的呢?在Ring3级下,微软就为我们内置了一个Hook窗口消息的API,也就是SetWindowsHookEx函数,这个 ...

  7. git自定义项目钩子和全局钩子

    钩子介绍 自定义钩子分为:项目钩子和全局钩子 自定义全局钩子: 全局钩子目录结构: (注意:excludes目录结构是我们自定义的目录,规则逻辑在update.d/update.py脚本里实现的,非g ...

  8. 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,局部钩子函数,全 ...

  9. Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解)

    我在之前做了一个关于AJAX和form组件的笔记,可以参考:Django学习笔记(8)——前后台数据交互实战(AJAX):Django学习笔记(6)——Form表单 我觉得自己在写Django笔记(8 ...

随机推荐

  1. [Ramda] Curry, Compose and Pipe examples

    const curry = R.curry((fns, ary) => R.ap(fns, ary)); ), R.add()]); ,,]); console.log(res); //[2, ...

  2. 浅谈python字符串存储形式

    http://blog.csdn.net/zhonghuan1992 钟桓 2014年8月31日 浅谈python字符串存储形式 记录一下自己今的天发现疑问而且给出自己现有知识有的回答. 长话短说,用 ...

  3. Android 自动弹出软键盘(输入键盘)

    很多应用中对于一个界面比如进入搜索界面或者修改信息等等情况,为了用户体验应该自动弹出软键盘而不是让用户主动点击输入框才弹出(因为用户进入该界面必然是为了更改信息).具体实现这种效果如下: EditTe ...

  4. [NPM] Add comments to your npm scripts

    The need for comments in your package.json file becomes desirable the more and more npm scripts you ...

  5. ssh连接上腾讯云、华为云Linux服务器,一会就自动断开

    客户端向服务端发送心跳 依赖 ssh 客户端定时发送心跳,putty.SecureCRT.XShell 都有这个功能. Linux / Unix 下,编辑 ssh 配置文件: # vim /etc/s ...

  6. hadoop 3.x 关闭安全模式

    hdfs启动后发现进入了安全模式,最开始使用hdfs dfsadmin -safemode leave来进行关闭发现无法关闭,只好使用hdfs dfsadmin -safemode forceExit ...

  7. 前端后端分离,怎么解决SEO优化的问题呢?

    对于90%以上的互联网公司来说,前后端分离是必须要做的.目前接手的公司的一个工程,后端是PHP,用的smarty模板,开发效率和之前公司的完全分离相比,确实低不少,一方面需要前端会PHP,另一方面沟通 ...

  8. Application与Context关联

    在发展中Application分类,几乎每个项目将用于,它的四个组成部分的实施优化,它是一个全球性的框,这可能是这么理解,例如,我们可以application类存储一些变量,在其他类访问,例如,在A类 ...

  9. VS2010中新控件的编程------颜色按钮类和颜色对话框

    (1)      颜色按钮类和颜色对话框 1) 颜色对话框 MFC提供了颜色对话框类CMFCColorDialog进行颜色的选择,系统可以利用DoModal()调用,然后选择相应的颜色. CMFCCo ...

  10. NET SignalR2

    .NET SignalR2持久连接层解析   越是到年底越是感觉浑身无力,看着啥也不想动,只期盼着年终奖的到来以此来给自己打一针强心剂.估摸着大多数人都跟我一样犯着这样浑身无力的病,感觉今年算是没挣到 ...