原文:c# 全局钩子实现扫码枪获取信息。

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. public struct BarCodes
  23. {
  24. public int VirtKey;//虚拟吗
  25. public int ScanCode;//扫描码
  26. public string KeyName;//键名
  27. public uint Ascll;//Ascll
  28. public char Chr;//字符
  29.  
  30. public string BarCode;//条码信息 保存最终的条码
  31. public bool IsValid;//条码是否有效
  32. public DateTime Time;//扫描时间,
  33. }
  34.  
  35. private struct EventMsg
  36. {
  37. public int message;
  38. public int paramL;
  39. public int paramH;
  40. public int Time;
  41. public int hwnd;
  42. }
  43.  
  44. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  45. private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
  46.  
  47. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  48. private static extern bool UnhookWindowsHookEx(int idHook);
  49.  
  50. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  51. private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
  52.  
  53. [DllImport("user32", EntryPoint = "GetKeyNameText")]
  54. private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize);
  55.  
  56. [DllImport("user32", EntryPoint = "GetKeyboardState")]
  57. private static extern int GetKeyboardState(byte[] pbKeyState);
  58.  
  59. [DllImport("user32", EntryPoint = "ToAscii")]
  60. private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);
  61.  
  62. [DllImport("kernel32.dll")]
  63. public static extern IntPtr GetModuleHandle(string name);
  64.  
  65. delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
  66. BarCodes barCode = new BarCodes();
  67. int hKeyboardHook = 0;
  68. string strBarCode = "";
  69.  
  70. private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
  71. {
  72. if (nCode == 0)
  73. {
  74. EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
  75. if (wParam == 0x100)//WM_KEYDOWN=0x100
  76. {
  77. barCode.VirtKey = msg.message & 0xff;//虚拟吗
  78. barCode.ScanCode = msg.paramL & 0xff;//扫描码
  79. StringBuilder strKeyName = new StringBuilder(225);
  80. if (GetKeyNameText(barCode.ScanCode * 65536, strKeyName, 255) > 0)
  81. {
  82. barCode.KeyName = strKeyName.ToString().Trim(new char[] { ' ', '\0' });
  83. }
  84. else
  85. {
  86. barCode.KeyName = "";
  87. }
  88. byte[] kbArray = new byte[256];
  89. uint uKey = 0;
  90. GetKeyboardState(kbArray);
  91.  
  92. if (ToAscii(barCode.VirtKey, barCode.ScanCode, kbArray, ref uKey, 0))
  93. {
  94. barCode.Ascll = uKey;
  95. barCode.Chr = Convert.ToChar(uKey);
  96. }
  97.  
  98. TimeSpan ts = DateTime.Now.Subtract(barCode.Time);
  99.  
  100. if (ts.TotalMilliseconds > 50)
  101. {//时间戳,大于50 毫秒表示手动输入
  102. strBarCode = barCode.Chr.ToString();
  103. }
  104. else
  105. {
  106. if ((msg.message & 0xff) == 13 && strBarCode.Length > 3)
  107. {//回车
  108. barCode.BarCode = strBarCode;
  109. barCode.IsValid = true;
  110. }
  111. strBarCode += barCode.Chr.ToString();
  112. }
  113. barCode.Time = DateTime.Now;
  114. if (BarCodeEvent != null)
  115. BarCodeEvent(barCode);//触发事件
  116. barCode.IsValid = false;
  117. }
  118. }
  119. return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
  120. }
  121.  
  122. //安装钩子
  123. public bool Start()
  124. {
  125. if (hKeyboardHook == 0)
  126. {
  127. hookproc = new HookProc(KeyboardHookProc);
  128.  
  129. //GetModuleHandle 函数 替代 Marshal.GetHINSTANCE
  130. //防止在 framework4.0中 注册钩子不成功
  131. IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
  132.  
  133. //WH_KEYBOARD_LL=13
  134. //全局钩子 WH_KEYBOARD_LL
  135. // hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
  136.  
  137. hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0);
  138. }
  139. return (hKeyboardHook != 0);
  140. }
  141.  
  142. //卸载钩子
  143. public bool Stop()
  144. {
  145. if (hKeyboardHook != 0)
  146. {
  147. return UnhookWindowsHookEx(hKeyboardHook);
  148. }
  149. return true;
  150. }
  151. }
  152. }

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

版本二:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6.  
  7. namespace BarcodeScanner
  8. {
  9. public class ScanerHook
  10. {
  11. public delegate void ScanerDelegate(ScanerCodes codes);
  12. 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

  1. //private static int HookProc(int nCode, Int32 wParam, IntPtr lParam);
  2. private int hKeyboardHook = 0;//声明键盘钩子处理的初始值
  3. private ScanerCodes codes = new ScanerCodes();//13为键盘钩子
  4. //定义成静态,这样不会抛出回收异常
  5. private static HookProc hookproc;
  6. delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
  7. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
         //设置钩子
  8. private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
  9. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
         //卸载钩子
  10. private static extern bool UnhookWindowsHookEx(int idHook);
  11. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    //继续下个钩子
  12. private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
  13.  
  14. [DllImport("user32", EntryPoint = "GetKeyNameText")]
  15. private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize);
  16. [DllImport("user32", EntryPoint = "GetKeyboardState")]
         //获取按键的状态
  17. private static extern int GetKeyboardState(byte[] pbKeyState);
  18. [DllImport("user32", EntryPoint = "ToAscii")]
         //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
  19. 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.

  1.  

        

  1. [DllImport("kernel32.dll")]
         //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
  2. public static extern IntPtr GetModuleHandle(string name);
  3. public ScanerHook()
  4. {
  5. }
  6. public bool Start()
  7. {
  8. if (hKeyboardHook == 0)
  9. {
  10. hookproc = new HookProc(KeyboardHookProc);
  11. //GetModuleHandle 函数 替代 Marshal.GetHINSTANCE
  12. //防止在 framework4.0中 注册钩子不成功
  13. IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
  14. //WH_KEYBOARD_LL=13
  15. //全局钩子 WH_KEYBOARD_LL
  16. // hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
  17. hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0);
  18. }
  19. return (hKeyboardHook != 0);
  20. }
  21. public bool Stop()
  22. {
  23. if (hKeyboardHook != 0)
  24. {
  25. bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
  26. hKeyboardHook = 0;
  27. return retKeyboard;
  28.  
  29. }
  30. return true;
  31. }
  32. private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
  33. {
  34.  
  35. EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
  36. codes.Add(msg);
  37. if (ScanerEvent != null && msg.message == 13 && msg.paramH > 0 && !string.IsNullOrEmpty(codes.Result))
  38. {
  39. ScanerEvent(codes);
  40. }
  41. return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
  42. }
  43. public class ScanerCodes
  44. {
  45. private int ts = 100; // 指定输入间隔为300毫秒以内时为连续输入
  46. private List<List<EventMsg>> _keys = new List<List<EventMsg>>();
  47. private List<int> _keydown = new List<int>(); // 保存组合键状态
  48. private List<string> _result = new List<string>(); // 返回结果集
  49. private DateTime _last = DateTime.Now;
  50. private byte[] _state = new byte[256];
  51. private string _key = string.Empty;
  52. private string _cur = string.Empty;
  53. public EventMsg Event
  54. {
  55. get
  56. {
  57. if (_keys.Count == 0)
  58. {
  59. return new EventMsg();
  60. }
  61. else
  62. {
  63. return _keys[_keys.Count - 1][_keys[_keys.Count - 1].Count - 1];
  64. }
  65. }
  66. }
  67. public List<int> KeyDowns
  68. {
  69. get
  70. {
  71. return _keydown;
  72. }
  73. }
  74. public DateTime LastInput
  75. {
  76. get
  77. {
  78. return _last;
  79. }
  80. }
  81. public byte[] KeyboardState
  82. {
  83. get
  84. {
  85. return _state;
  86. }
  87. }
  88. public int KeyDownCount
  89. {
  90. get
  91. {
  92. return _keydown.Count;
  93. }
  94. }
  95. public string Result
  96. {
  97. get
  98. {
  99. if (_result.Count > 0)
  100. {
  101. return _result[_result.Count - 1].Trim();
  102. }
  103. else
  104. {
  105. return null;
  106. }
  107. }
  108. }
  109. public string CurrentKey
  110. {
  111. get
  112. {
  113. return _key;
  114. }
  115. }
  116. public string CurrentChar
  117. {
  118. get
  119. {
  120. return _cur;
  121. }
  122. }
  123. public bool isShift
  124. {
  125. get
  126. {
  127. return _keydown.Contains(160);
  128. }
  129. }
  130. public void Add(EventMsg msg)
  131. {
  132. #region 记录按键信息
  133.  
  134. // 首次按下按键
  135. if (_keys.Count == 0)
  136. {
  137. _keys.Add(new List<EventMsg>());
  138. _keys[0].Add(msg);
  139. _result.Add(string.Empty);
  140. }
  141. // 未释放其他按键时按下按键
  142. else if (_keydown.Count > 0)
  143. {
  144. _keys[_keys.Count - 1].Add(msg);
  145. }
  146. // 单位时间内按下按键
  147. else if (((TimeSpan)(DateTime.Now - _last)).TotalMilliseconds < ts)
  148. {
  149. _keys[_keys.Count - 1].Add(msg);
  150. }
  151. // 从新记录输入内容
  152. else
  153. {
  154. _keys.Add(new List<EventMsg>());
  155. _keys[_keys.Count - 1].Add(msg);
  156. _result.Add(string.Empty);
  157. }
  158. #endregion
  159. _last = DateTime.Now;
  160. #region 获取键盘状态
  161. // 记录正在按下的按键
  162. if (msg.paramH == 0 && !_keydown.Contains(msg.message))
  163. {
  164. _keydown.Add(msg.message);
  165. }
  166. // 清除已松开的按键
  167. if (msg.paramH > 0 && _keydown.Contains(msg.message))
  168. {
  169. _keydown.Remove(msg.message);
  170. }
  171. #endregion
  172. #region 计算按键信息
  173.  
  174. int v = msg.message & 0xff;
  175. int c = msg.paramL & 0xff;
  176. StringBuilder strKeyName = new StringBuilder(500);
  177. if (GetKeyNameText(c * 65536, strKeyName, 255) > 0)
  178. {
  179. _key = strKeyName.ToString().Trim(new char[] { ' ', '\0' });
  180. GetKeyboardState(_state);
  181. if (_key.Length == 1 && msg.paramH == 0)// && msg.paramH == 0
  182. {
  183. // 根据键盘状态和shift缓存判断输出字符
  184. _cur = ShiftChar(_key, isShift, _state).ToString();
  185. _result[_result.Count - 1] += _cur;
  186. }
                  // 备选
               else
  187. {
  188. _cur = string.Empty;
  189. }
  190. }
  191. #endregion
  192. }
  193. private char ShiftChar(string k, bool isShiftDown, byte[] state)
  194. {
  195. bool capslock = state[0x14] == 1;
  196. bool numlock = state[0x90] == 1;
  197. bool scrolllock = state[0x91] == 1;
  198. bool shiftdown = state[0xa0] == 1;
  199. char chr = (capslock ? k.ToUpper() : k.ToLower()).ToCharArray()[0];
  200. if (isShiftDown)
  201. {
  202. if (chr >= 'a' && chr <= 'z')
  203. {
  204. chr = (char)((int)chr - 32);
  205. }
  206. else if (chr >= 'A' && chr <= 'Z')
  207. {
  208. if (chr=='Z')
  209. {
  210. string s = "";
  211. }
  212. chr = (char)((int)chr + 32);
  213. }
  214. else
  215. {
  216. string s = "`1234567890-=[];',./";
  217. string u = "~!@#$%^&*()_+{}:\"<>?";
  218. if (s.IndexOf(chr) >= 0)
  219. {
  220. return (u.ToCharArray())[s.IndexOf(chr)];
  221. }
  222. }
  223. }
  224. return chr;
  225. }
  226. }
  227. public struct EventMsg
  228. {
  229. public int message;
  230. public int paramL;
  231. public int paramH;
  232. public int Time;
  233. public int hwnd;
  234. }
  235. }
  236. }

版本二中的代码,实践中发现出现了获取扫描数据却省略“+”加号的情况出现。

因此在版本二中备选处添加

  1.            //判断是+ 强制添加+
  2. else if (_key.Length == 5 && msg.paramH == 0&&msg.paramL==78&&msg.message==107)// && msg.paramH == 0
  3. {
  4. // 根据键盘状态和shift缓存判断输出字符
  5. _cur = Convert.ToChar('+').ToString();
  6. _result[_result.Count - 1] += _cur;
  7. }

3.winform在无焦点情况下的使用方式:

  1. using BarcodeScanner;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Drawing;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Windows.Forms;
  10.  
  11. namespace BarCodeTest
  12. {
  13. public partial class Form1 : Form
  14. {
  15. private ScanerHook listener = new ScanerHook();
  16. public Form1()
  17. {
  18. InitializeComponent();
  19. listener.ScanerEvent += Listener_ScanerEvent;
  20. }
  21. private void Listener_ScanerEvent(ScanerHook.ScanerCodes codes)
  22. {
  23. textBox3.Text = codes.Result;
  24. }
  25. private void Form1_Load(object sender, EventArgs e)
  26. {
  27. listener.Start();
  28. }
  29.  
  30. private void Form1_FormClosed(object sender, FormClosedEventArgs e)
  31. {
  32. listener.Stop();
  33. }
  34. }
  35. }

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. 在linux,windows上安装ruby on rails开发环境

    ruby是一个非常优秀的语言,ruby的精髓rails可以让web开发的效率成倍的提高,下面就介绍一下我搭建rails环境的过程.windows下搭建ruby rails web开发环境本篇文章主要是 ...

  2. 《转》couldn&#39;t connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145

    couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145,有须要的朋友能够參考下. 应为昨天安装的时候没及时 ...

  3. MySQL的表空间管理

    表空间: MySQL没有真正意义上的表空间管理. MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间. 一般来说,当数据量很小的时候建议使用共享表空间的管理方式. ...

  4. Snmp常用oid

    http://blog.csdn.net/youngqj/article/details/7311849 系统参数(1.3.6.1.2.1.1)   OID 描述 备注 请求方式 .1.3.6.1.2 ...

  5. [NPM] Create a node script to replace a complex npm script

    In this lesson we will look at pulling out complex npm script logic into an external JavaScript file ...

  6. CoreLocation定位

    nCoreLocation   n简介 n在移动互联网时代,移动app能解决用户的很多生活琐事,比如 p导航:去任意陌生的地方 p周边:找餐馆.找酒店.找银行.找电影院 p n在上述应用中,都用到了地 ...

  7. Opencv 使用Stitcher类图像拼接生成全景图像

    Opencv中自带的Stitcher类可以实现全景图像,效果不错.下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到: \opencv\sourc ...

  8. hadoop 3.x Replication与Availability不一致

    看下面的文字前先确保你的Replication值不大于你设置的虚拟机数量 如图,显示的副本数为3,但是实际可用的只有一台机器,查看了下hadoop003,hadoop004两台机器,果然没有存储数据, ...

  9. KindEditor4.1.10,支持粘贴图片

    转载自https://blog.csdn.net/jimmy0021/article/details/73251406 我已经忘记我是不是从这个博主的那里找到的解决kindeditor粘贴图片的方法了 ...

  10. Swift 中的高阶函数和函数嵌套

    高阶函数 在Swift中,函数可做为“一等公民”的存在,也就意味着,我们可以和使用 int 以及 String 一样,将函数当做 参数.值.类型来使用. 其中,将函数当作一个参数和值来使用可见下: t ...