首页 > 编程语言 >使用c#捕获usb扫描枪扫描二维码、条形码结果

使用c#捕获usb扫描枪扫描二维码、条形码结果

时间:2023-05-23 14:14:11浏览次数:61  
标签:扫描枪 return usb c# private int msg message public

起因

  最近公司买了一些扫描枪,要做个展会门票扫描,门票格式为一个网址,生成方式是qr二维码

  以前没玩过这东西,尝试一下看看,结果发现扫描枪根本就是个纯输入设备,和键盘一个性质

  好吧,先不管这些,把扫描结果记录下来才是正经的,于是在网上搜了一下“c# winform 无焦点监听键盘输入”,然后一大堆同质化的内容,虽然包含了usb扫描枪数据捕捉,但是。。。。这tmd的全是条形码的!

  好吧,根据这个咱们自己调整调整吧
代码实现

  先发一个类,用来支持扫描枪扫描结果获取
  

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
 
namespace WinForm_Scaner
{
    public class ScanerHook
    {
        public delegate void ScanerDelegate(ScanerCodes codes);
        public event ScanerDelegate ScanerEvent;
        delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        private int hKeyboardHook = 0;
        private ScanerCodes codes = new ScanerCodes();
        private HookProc hookproc;
        [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", 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);
        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)
            {
                return UnhookWindowsHookEx(hKeyboardHook);
            }
            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 0;
        }
        public class ScanerCodes
        {
            private int ts = 300; // 指定输入间隔为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)
                    {
                        // 根据键盘状态和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')
                    {
                        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;
        }
    }
}

分析需求的不同之处

基本原理与其他文章的没有什么不同,区别在于,扫描二维码时,结果不仅仅是数字,并且字母有大小写区分,还可能包含标点符号什么的

当然,我这个程序没有对8位ascii以上的字符集做支持,也就是不支持汉字什么的,需要对汉字支持的自己进行魔改好了


实现的过程解析

KeyboardHookProc 是用来捕捉键盘输入时间的,我的处理方式和网上其他文章的区别在于,每次捕捉到的内容都直接扔到数组中去,然后再触发事件返回给调用者,而扔给数组的方法就是Add方法了,这里的处理才是真正的核心

因为条形码中只有数字和部分符号,所以基本可以不用考虑键盘状态或组合键的情况,但二维码就不能不考虑了,尤其是有些验证信息还区分大小写的情况,例如二维码扫描结果是这样的内容“c=a0$nLD7_49vSF9-1&n=1”,我使用的是科密ew-9200扫描枪,因为要区分扫描枪,所以对每个扫描枪追加的后缀设置,也就是&n=1部分,前面的才是扫描结果

那么通过事件监听,发现在键盘状态为小写时,a还是a,但是$是4,L是l,也就是说,我们无法通过键盘状态来获取我们的预期结果,但是我们发现了另一个现象,那就是相邻的几个EventMsg中paramH为0时,是表示按下了对应按钮,paramH大于零时表示松开了对应按钮,那么就有一个有意思的现象了,当出现字母a时,EventMsg中message相同的两次,一次paramH=0,一次paramH=128,而出现字母L时,相关的EventMsg为四个,分别是message=160 & paramH=0 , message=76 & paramH=0 , message=76 & paramH=128 , message=160 & paramH=129,于是就有了一个辅助记录是否组合键的变量及代码段

                #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

 

可以根据_keydown.Count来判断当前按下了几个键,可以使用_keydown.Contains(160)判断是否按下了Shift键,然后使用ShiftChar方法来获取最终的按键对应字符

至于结果保存,我使用了一个数组List<List<EventMsg>>,对每次输入都单独进行记录,大家也可以自行修改以符合自己的习惯

最后,就是事件触发了

            if (ScanerEvent != null && msg.message == 13 && msg.paramH > 0 && !string.IsNullOrEmpty(codes.Result))
            {
                ScanerEvent(codes);
            }

当触发事件有定义,且当前按键时回车键松开,且已记录的字符串非空,则触发返回事件,其实这么看起来,感觉还是不算很难

好了,类说明大概就这样了,调用方法就更简单了

    public partial class Scaner : Form
    {
        private ScanerHook listener = new ScanerHook();
        public Scaner()
        {
            InitializeComponent();
            listener.ScanerEvent += Listener_ScanerEvent;
        }
        private void Listener_ScanerEvent(ScanerHook.ScanerCodes codes)
        {
            dgv_lst.Rows.Add(new object[] { codes.KeyDownCount, codes.Event.message, codes.Event.paramH, codes.Event.paramL, codes.CurrentChar, codes.Result, codes.isShift, codes.CurrentKey });
        }
        private void Scaner_Load(object sender, System.EventArgs e)
        {
            listener.Start();
        }
        private void Scaner_FormClosing(object sender, FormClosingEventArgs e)
        {
            listener.Stop();
        }
    }

直接实例化一个ScanerHook,然后Start开始监听,对ScanerEvent进行接收就可以了,至于是要保存还是要发送请求,亦或者是显示到winform里,那都不是事,自己玩去吧


原文链接:https://blog.csdn.net/superwfei/article/details/79212782

标签:扫描枪,return,usb,c#,private,int,msg,message,public
From: https://www.cnblogs.com/alannxu/p/17425014.html

相关文章

  • 6、CSRF漏洞管理
    一、CSRF概念答:全称是Cross-siterequestforgery,跨站请求伪造,我们可以理解未这种漏洞为攻击者利用被攻击者的身份发起了某些被攻击者原本不知情的网络请求,包括以被攻击者的身份发布微博,留言等。CSRF能够发邮件,发消息,盗取账户,购买商品,虚拟货币转账等。二、CSRF漏洞原理......
  • react+mock
    之前写过一篇react项目添加mock的文章,在文章中说的mock代码是打包配置的时候写入的,这种方式导致每次修改mock数据后都需要重新启动服务才能获取到新的mock数据。今天使用另外一种方式来实现开发过程中的数据mock。在项目中安装axios和mock.js两个框架然后在webpack配置文件中......
  • 使用c#捕获usb扫描枪扫描二维码、条形码结果(支持中文版)
    前因之前在18年写过一个使用c#捕获usb扫描枪扫描二维码、条形码结果,当时我是没有遇到需要使用中文的情况,因为二维码都是我自己控制生成的,如果遇到中文,我会提前进行编码编程unicode编码,所以,没有专门针对中文符号做支持但一年多以来,不少人询问,或者在博客下留言,提出了中文扫码支持的......
  • Let’s Encrypted:一键申请SSL证书脚本
    wgethttps://raw.githubusercontent.com/nanqinlang-script/acme/master/acme_2.0.shbashacme_2.0.sh使用说明输入你的域名选择要生成证书的类型。输入数字0(生成RSA类型)或1(生成ECC类型)。若未选择则默认生成RSA类型用DNS方式验证域名所有权:添加脚本中显示的绿......
  • 直播app开发搭建,scroll-view封装自定义下拉刷新
    直播app开发搭建,scroll-view封装自定义下拉刷新完整代码 <!--container.vue--><template><view><scroll-viewscroll-y="true"class="scroll-view":refresher-enabled="refresh":refresher-triggered="triggered":refresher-t......
  • 1万多贺卡祝福节日祝福ACCESS\EXCEL数据库
    你是否在春节只能复制别人的祝福短信来发送?你是否在节日期间为给朋友送祝福无从下手?你是否在特殊日子和朋友打不开对话的开头?其实,很多情况下我们都需要一堆不同与别的人祝福语,用在节日上、用在交际上等日常备用。今天这一份数据就是祝福语大全,有节日、祝福两大类,子分类有50多个。......
  • CF1196F K-th Path 题解 floyd
    题目链接:https://codeforces.com/problemset/problem/1196/F题目大意:给定一个包含\(n\)个节点\(m\)条边的无向图(\(n,m\le2\cdot10^5\))。图中任意两点之间(如果连通的话)都存在一条最短路(节点\(i\)到它自己不算最短路,\(i\)到\(j\)的最短路和\(j\)到\(i\)的最短......
  • BootStrap导入excel
    BootStrap_实现导入Excel(BootStrap-InputFile)【实例】weixin_40877388于2020-03-3114:52:47发布5505收藏19分类专栏:BootStrap版权BootStrap专栏收录该内容2篇文章0订阅订阅专栏一、前言在批量加入学生信息的时候,我们通常采用Excel导入的方式,方便,快......
  • js导入excel&导出excel
    js导入excel&导出excel Excel导入html代码<buttonstyle={{color:'#1890ff',fontSize:'14px',cursor:'pointer'}}onClick={()=>{upFile();}}>导入</button><inputid="upFile"type="fil......
  • https部署与docker下nginx的转发+文件获取
    进入nginx容器查看是否有etc/nginx/cert目录将https的域名证书放到cert目录下配置80和443server{#listen80;#侦听80端口listen80;#侦听443端口,用于SSLserver_name127.zyfdtsite.com;client_max_body_size1024m;#charsetkoi8-r;#acc......