zoukankan      html  css  js  c++  java
  • [C#.Net]全局钩子实现USB扫码枪无焦点状态下扫入

    1.扫描枪获取数据原理基本相当于键盘数据,获取扫描枪扫描出来的数据,一般分为两种实现方式。

      a)文本框输入获取焦点,扫描后自动显示在文本框内。

      b)使用键盘钩子,勾取扫描枪虚拟按键,根据按键频率进行手动输入和扫描枪扫描判断。

    2.要实现系统钩子其实很简单,调用三个Win32的API即可。

    SetWindowsHookEx 用于设置钩子。(设立一道卡子,盘查需要的信息)

    CallNextHookEx 用于传递钩子(消息是重要的,所以从哪里来,就应该回到哪里去,除非你决定要封锁消息)

    UnhookWindowsHookEx 卸载钩子(卸载很重要,卡子设多了会造成拥堵)

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Text;
      4 using System.Runtime.InteropServices;
      5 using System.Reflection;
      6 using System.Diagnostics;
      7 namespace SaomiaoTest2
      8 {
      9     /// <summary>
     10     /// 获取键盘输入或者USB扫描枪数据 可以是没有焦点 应为使用的是全局钩子
     11     /// USB扫描枪 是模拟键盘按下
     12     /// 这里主要处理扫描枪的值,手动输入的值不太好处理
     13     /// </summary>
     14    public class BardCodeHooK
     15     {
     16         public delegate void BardCodeDeletegate(BarCodes barCode);
     17         public event BardCodeDeletegate BarCodeEvent;
     18 
     19         //定义成静态,这样不会抛出回收异常
     20         private static HookProc hookproc;
     21 
     22 
     23         public struct BarCodes
     24         {
     25             public int VirtKey;//虚拟吗
     26             public int ScanCode;//扫描码
     27             public string KeyName;//键名
     28             public uint Ascll;//Ascll
     29             public char Chr;//字符
     30 
     31             public string BarCode;//条码信息   保存最终的条码
     32             public bool IsValid;//条码是否有效
     33             public DateTime Time;//扫描时间,
     34         }
     35 
     36         private struct EventMsg
     37         {
     38             public int message;
     39             public int paramL;
     40             public int paramH;
     41             public int Time;
     42             public int hwnd;
     43         }
     44 
     45         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     46         private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
     47 
     48         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     49         private static extern bool UnhookWindowsHookEx(int idHook);
     50 
     51         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     52         private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
     53 
     54         [DllImport("user32", EntryPoint = "GetKeyNameText")]
     55         private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize);
     56 
     57         [DllImport("user32", EntryPoint = "GetKeyboardState")]
     58         private static extern int GetKeyboardState(byte[] pbKeyState);
     59 
     60         [DllImport("user32", EntryPoint = "ToAscii")]
     61         private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);
     62 
     63         [DllImport("kernel32.dll")]
     64         public static extern IntPtr GetModuleHandle(string name);
     65 
     66 
     67         delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
     68         BarCodes barCode = new BarCodes();
     69         int hKeyboardHook = 0;
     70         string strBarCode = "";
     71 
     72         private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
     73         {
     74             if (nCode == 0)
     75             {
     76                 EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
     77                 if (wParam == 0x100)//WM_KEYDOWN=0x100
     78                 {
     79                     barCode.VirtKey = msg.message & 0xff;//虚拟吗
     80                     barCode.ScanCode = msg.paramL & 0xff;//扫描码
     81                     StringBuilder strKeyName = new StringBuilder(225);
     82                     if (GetKeyNameText(barCode.ScanCode * 65536, strKeyName, 255) > 0)
     83                     {
     84                         barCode.KeyName = strKeyName.ToString().Trim(new char[] { ' ', '' });
     85                     }
     86                     else
     87                     {
     88                         barCode.KeyName = "";
     89                     }
     90                     byte[] kbArray = new byte[256];
     91                     uint uKey = 0;
     92                     GetKeyboardState(kbArray);
     93 
     94 
     95                     if (ToAscii(barCode.VirtKey, barCode.ScanCode, kbArray, ref uKey, 0))
     96                     {
     97                         barCode.Ascll = uKey;
     98                         barCode.Chr = Convert.ToChar(uKey);
     99                     }
    100 
    101                     TimeSpan ts = DateTime.Now.Subtract(barCode.Time);
    102 
    103                     if (ts.TotalMilliseconds > 50)
    104                     {//时间戳,大于50 毫秒表示手动输入
    105                         strBarCode = barCode.Chr.ToString();
    106                     }
    107                     else
    108                     {
    109                         if ((msg.message & 0xff) == 13 && strBarCode.Length > 3)
    110                         {//回车
    111                             barCode.BarCode = strBarCode;
    112                             barCode.IsValid = true;
    113                         }
    114                         strBarCode += barCode.Chr.ToString();
    115                     }
    116                     barCode.Time = DateTime.Now;
    117                     if (BarCodeEvent != null)
    118                         BarCodeEvent(barCode);//触发事件
    119                     barCode.IsValid = false;
    120                 }
    121             }
    122             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
    123         }
    124 
    125         //安装钩子
    126         public bool Start()
    127         {
    128             if (hKeyboardHook == 0)
    129             {
    130                 hookproc = new HookProc(KeyboardHookProc);
    131 
    132 
    133                 //GetModuleHandle 函数 替代 Marshal.GetHINSTANCE
    134                 //防止在 framework4.0中 注册钩子不成功
    135                 IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
    136 
    137                 //WH_KEYBOARD_LL=13
    138                 //全局钩子 WH_KEYBOARD_LL
    139                 //  hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
    140 
    141                 hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0);
    142             }
    143             return (hKeyboardHook != 0);
    144         }
    145 
    146         //卸载钩子
    147         public bool Stop()
    148         {
    149             if (hKeyboardHook != 0)
    150             {
    151                 return UnhookWindowsHookEx(hKeyboardHook);
    152             }
    153             return true;
    154         }
    155     }
    156 }

    在实践过程中,发现版本一的代码只能扫描条形码,如伴随二维码中的字母出现就不能正确获取数据。

    版本二:

    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[] { ' ', '' });
                        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();  
            }  
        }
    }

    其实上面的代码都是复制这位大侠的,真心觉得大侠太厉害了,解决多年的疑惑,保留纪念

    https://www.cnblogs.com/TBW-Superhero/p/8659306.html

  • 相关阅读:
    windows系统切换jdk,修改java_home无效情况
    Cannot instantiate interface org.springframework.context.ApplicationListener
    MySQL分组查询获取每个学生前n条分数记录(分组查询前n条记录)
    ASP.NET Web API 使用Swagger生成在线帮助测试文档,支持多个GET
    EF TO MYSQL 无法查询中文的解决方法
    HttpWebRequest post请求获取webservice void数据信息
    This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms. 此实现不是 Windows 平台 FIPS 验证的加密算法的一部分 解决方案
    MySQL 5.7.13解压版安装记录 mysql无法启动教程
    C# udpclient 发送数据断网后自动连接的方法
    汽车XX网站秒杀抢购代码
  • 原文地址:https://www.cnblogs.com/masonlu/p/10105135.html
Copyright © 2011-2022 走看看