C#--USB扫码枪实现无焦点状态下扫入
以下内容是复制这个大神的文章,解决了USB扫码枪无焦点扫码的问题,非常感谢,我留个笔记
https://www.cnblogs.com/TBW-Superhero/p/8659306.html
1.扫描枪获取数据原理基本相当于键盘数据,获取扫描枪扫描出来的数据,一般分为两种实现方式。
a)文本框输入获取焦点,扫描后自动显示在文本框内。
b)使用键盘钩子,勾取扫描枪虚拟按键,根据按键频率进行手动输入和扫描枪扫描判断。
2.要实现系统钩子其实很简单,调用三个Win32的API即可。
SetWindowsHookEx 用于设置钩子。(设立一道卡子,盘查需要的信息)
CallNextHookEx 用于传递钩子(消息是重要的,所以从哪里来,就应该回到哪里去,除非你决定要封锁消息)
UnhookWindowsHookEx 卸载钩子(卸载很重要,卡子设多了会造成拥堵)
3,代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.Runtime.InteropServices; namespace Helper { public class BarcodeScannerHelper { //public delegate void ScanerDelegate(ScanerCodes codes); public delegate void ScanerDelegate( string 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); /// <summary> /// 声明键盘钩子处理的初始值 /// </summary> 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 BarcodeScannerHelper() { } 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); ScanerEvent(codes.Result); } 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 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; } 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; } } } |
4,在项目中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | namespace Test { public partial class Form1 : Form { Helper.BarcodeScannerHelper scannerListener= new BarcodeScannerHelper(); public Form1() { scannerListener.ScanerEvent += ScannerListener_ScanerEvent; } private void ScannerListener_ScanerEvent( string codes) { this .ricScanContent2.AppendText(codes+ "\n" ); } private void Form1_Load( object sender, EventArgs e) { this .scannerListener.Start(); } private void Form1_FormClosing( object sender, FormClosingEventArgs e) { this .scannerListener.Stop(); } } } |
分类:
C#--杂货
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构