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

目录

中文二维码扫码处理

起因

代码实现

分析需求的不同之处

实现的过程解析


中文二维码扫码处理

关于扫描枪对包含中文的二维码扫描的处理,请参考使用c#捕获usb扫描枪扫描二维码、条形码结果(支持中文版),本文之后的评论不再回复关于中文扫码的问题

另外,请仔细阅读本文,因为中文支持博文仅修改了本文中的一个类,其他代码沿用本文

-----------------------------------------------------------------

起因

最近公司买了一些扫描枪,要做个展会门票扫描,门票格式为一个网址,生成方式是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里,那都不是事,自己玩去吧
 

  • 38
    点赞
  • 206
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 56
    评论
对于使用得力扫描枪扫描二维码,你可以使用以下步骤在C#中实现: 1. 确保你的得力扫描枪已经连接到计算机,并且被识别为HID(Human Interface Device)设备。 2. 使用Windows API来读取扫描枪的输入数据。你可以通过使用`RegisterRawInputDevices`函数来注册输入设备,并使用`WM_INPUT`消息来接收输入数据。 下面是一个基本的示例代码,演示了如何使用C#来处理得力扫描枪的输入数据: ```csharp using System; using System.Runtime.InteropServices; using System.Windows.Forms; class Program { private const int WM_INPUT = 0x00FF; private const int RID_INPUT = 0x10000003; private const int RIDEV_INPUTSINK = 0x00000100; private const int RIDEV_DEVNOTIFY = 0x00002000; [StructLayout(LayoutKind.Sequential)] struct RAWINPUTDEVICELIST { public IntPtr hDevice; public int dwType; } [DllImport("user32.dll")] static extern uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint uiNumDevices, uint cbSize); [DllImport("user32.dll")] static extern bool RegisterRawInputDevices(RAWINPUTDEVICELIST[] pRawInputDeviceList, uint uiNumDevices, uint cbSize); [DllImport("user32.dll")] static extern uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader); static void Main() { // 注册输入设备 RAWINPUTDEVICELIST[] rawInputDeviceList = new RAWINPUTDEVICELIST[1]; rawInputDeviceList[0].dwType = RID_INPUT; rawInputDeviceList[0].hDevice = IntPtr.Zero; if (!RegisterRawInputDevices(rawInputDeviceList, 1, (uint)Marshal.SizeOf<RAWINPUTDEVICELIST>())) { Console.WriteLine("无法注册输入设备。"); return; } // 创建窗口来接收输入消息 NativeWindow window = new NativeWindow(); window.CreateHandle(new CreateParams()); // 循环接收输入消息 while (true) { Application.DoEvents(); } } protected override void WndProc(ref Message m) { if (m.Msg == WM_INPUT) { // 读取输入数据 uint size = 0; GetRawInputData(m.LParam, RID_INPUT, IntPtr.Zero, ref size, (uint)Marshal.SizeOf<RAWINPUTHEADER>()); IntPtr buffer = Marshal.AllocHGlobal((int)size); try { GetRawInputData(m.LParam, RID_INPUT, buffer, ref size, (uint)Marshal.SizeOf<RAWINPUTHEADER>()); // 在这里处理扫描枪的输入数据 // 解析二维码数据并进行相关操作 // 例如,你可以通过ZXing库来解码二维码 // 你可以在这里将二维码数据传递给其他方法进行处理 ProcessQRCodeData(buffer); } finally { Marshal.FreeHGlobal(buffer); } } base.WndProc(ref m); } private void ProcessQRCodeData(IntPtr buffer) { // 在这里处理扫描枪输入的二维码数据 // 解析二维码并进行相关操作 // 例如,你可以使用ZXing库来解码二维码 // 以下是一个使用ZXing库解码二维码的示例 // 你需要安装ZXing.Net库,通过NuGet包管理器安装 ZXing.BarcodeReader barcodeReader = new ZXing.BarcodeReader(); ZXing.Result result = barcodeReader.Decode(new Bitmap(/* 这里是图像数据来源 */)); if (result != null) { string decodedText = result.Text; Console.WriteLine("解码结果: " + decodedText); } else { Console.WriteLine("未能解码二维码。"); } } } ``` 请注意,上述代码只是一个基本示例,你需要根据实际情况进行适当的修改和调整。在代码中,我们使用`WM_INPUT`消息来接收扫描枪的输入数据,并通过`RegisterRawInputDevices`函数注册输入设备。然后,我们创建了一个窗口来接收输入消息,并在`WndProc`方法中处理输入数据。你可以在`ProcessQRCodeData`方法中解析扫描枪输入的二维码数据。 希望这可以帮助到你!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文盲老顾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值