一.为什么需要全局键盘监听?

在某些情况下应用程序需要实现快捷键执行特定功能,例如大家熟知的QQ截图功能Ctrl+Alt+A快捷键,只要QQ程序在运行(无论是拥有焦点还是处于后台运行状态),都可以按下快捷键使用此功能…

这个时候在程序中添加键盘监听肯定不能满足需求了,当用户焦点不在App上时(如最小化,或者用户在处理其它事物等等)键盘监听就失效了

二.怎样才能实现全局键盘监听?

这里需要用到Windows API,源码如下:(可以作为一个工具类[KeyboardHook.cs]收藏起来)

  1 using System;
  2 using System.Runtime.InteropServices;
  3 using System.Windows.Forms;
  4 //using System.Windows.Input;
  5
  6 namespace WpfApplication1
  7 {
  8     /// <summary>
  9     /// 键盘钩子
 10     /// [以下代码来自某网友,并非本人原创]
 11     /// </summary>
 12     class KeyboardHook
 13     {
 14         public event System.Windows.Forms.KeyEventHandler KeyDownEvent;
 15         public event KeyPressEventHandler KeyPressEvent;
 16         public event System.Windows.Forms.KeyEventHandler KeyUpEvent;
 17
 18         public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
 19         static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
 20         //值在Microsoft SDK的Winuser.h里查询
 21         // http://www.bianceng.cn/Programming/csharp/201410/45484.htm
 22         public const int WH_KEYBOARD_LL = 13;   //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
 23         HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
 24         //键盘结构
 25         [StructLayout(LayoutKind.Sequential)]
 26         public class KeyboardHookStruct
 27         {
 28             public int vkCode;  //定一个虚拟键码。该代码必须有一个价值的范围1至254
 29             public int scanCode; // 指定的硬件扫描码的关键
 30             public int flags;  // 键标志
 31             public int time; // 指定的时间戳记的这个讯息
 32             public int dwExtraInfo; // 指定额外信息相关的信息
 33         }
 34         //使用此功能,安装了一个钩子
 35         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 36         public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
 37
 38
 39         //调用此函数卸载钩子
 40         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 41         public static extern bool UnhookWindowsHookEx(int idHook);
 42
 43
 44         //使用此功能,通过信息钩子继续下一个钩子
 45         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
 46         public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
 47
 48         // 取得当前线程编号(线程钩子需要用到)
 49         [DllImport("kernel32.dll")]
 50         static extern int GetCurrentThreadId();
 51
 52         //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
 53         [DllImport("kernel32.dll")]
 54         public static extern IntPtr GetModuleHandle(string name);
 55
 56         public void Start()
 57         {
 58             // 安装键盘钩子
 59             if (hKeyboardHook == 0)
 60             {
 61                 KeyboardHookProcedure = new HookProc(KeyboardHookProc);
 62                 hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
 63                 //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
 64                 //************************************
 65                 //键盘线程钩子
 66                 //SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
 67                 //键盘全局钩子,需要引用空间(using System.Reflection;)
 68                 //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
 69                 //
 70                 //关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函数将钩子加入到钩子链表中,说明一下四个参数:
 71                 //idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,
 72                 //线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的
 73                 //线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何
 74                 //消息后便调用这个函数。hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子
 75                 //程代码位于当前进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。threaded 与安装的钩子子程相关联的线程的标识符
 76                 //如果为0,钩子子程与所有的线程关联,即为全局钩子
 77                 //************************************
 78                 //如果SetWindowsHookEx失败
 79                 if (hKeyboardHook == 0)
 80                 {
 81                     Stop();
 82                     throw new Exception("安装键盘钩子失败");
 83                 }
 84             }
 85         }
 86         public void Stop()
 87         {
 88             bool retKeyboard = true;
 89
 90
 91             if (hKeyboardHook != 0)
 92             {
 93                 retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
 94                 hKeyboardHook = 0;
 95             }
 96
 97             if (!(retKeyboard)) throw new Exception("卸载钩子失败!");
 98         }
 99         //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
100         [DllImport("user32")]
101         public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
102                                          int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)
103                                          byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
104                                          byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
105                                          int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
106
107         //获取按键的状态
108         [DllImport("user32")]
109         public static extern int GetKeyboardState(byte[] pbKeyState);
110
111
112         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
113         private static extern short GetKeyState(int vKey);
114
115         private const int WM_KEYDOWN = 0x100;//KEYDOWN
116         private const int WM_KEYUP = 0x101;//KEYUP
117         private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN
118         private const int WM_SYSKEYUP = 0x105;//SYSKEYUP
119
120         private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
121         {
122             // 侦听键盘事件
123             if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
124             {
125                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
126                 // raise KeyDown
127                 if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
128                 {
129                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
130                     System.Windows.Forms.KeyEventArgs e = new System.Windows.Forms.KeyEventArgs(keyData);
131                     KeyDownEvent(this, e);
132                 }
133
134                 //键盘按下
135                 if (KeyPressEvent != null && wParam == WM_KEYDOWN)
136                 {
137                     byte[] keyState = new byte[256];
138                     GetKeyboardState(keyState);
139
140                     byte[] inBuffer = new byte[2];
141                     if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
142                     {
143                         KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
144                         KeyPressEvent(this, e);
145                     }
146                 }
147
148                 // 键盘抬起
149                 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
150                 {
151                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
152                     System.Windows.Forms.KeyEventArgs e = new System.Windows.Forms.KeyEventArgs(keyData);
153                     KeyUpEvent(this, e);
154                 }
155
156             }
157             //如果返回1,则结束消息,这个消息到此为止,不再传递。
158             //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
159             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
160         }
161         ~KeyboardHook()
162         {
163             Stop();
164         }
165     }
166 }

View Code

三.如何使用上面的工具类?

 1     public partial class hook : Window
 2     {
 3         KeyboardHook k_hook;
 4         public hook()
 5         {
 6             InitializeComponent();
 7             k_hook = new KeyboardHook();
 8             //k_hook.KeyDownEvent += new System.Windows.Forms.KeyEventHandler(hook_KeyDown);//钩住键按下
 9             k_hook.KeyPressEvent += K_hook_KeyPressEvent;
10             k_hook.Start();//安装键盘钩子
11         }
12
13         private void K_hook_KeyPressEvent(object sender, KeyPressEventArgs e)
14         {
15             //tb1.Text += e.KeyChar;
16             int i = (int)e.KeyChar;
17             System.Windows.Forms.MessageBox.Show(i.ToString());
18         }
19
20         private void hook_KeyDown(object sender, KeyEventArgs e)
21         {
22             tb1.Text += (char)e.KeyData;
23
24
25             //判断按下的键(Alt + A)
26             //if (e.KeyValue == (int)Keys.A && (int)System.Windows.Forms.Control.ModifierKeys == (int)Keys.Alt)
27             //{
28             //    System.Windows.Forms.MessageBox.Show("ddd");
29             //}
30         }
31
32         private void Window_Unloaded(object sender, RoutedEventArgs e)
33         {
34             k_hook.Stop();
35         }
36     }

View Code

//注意几种不同的键值判断: 
//1>.单普通键(例如A) 
//2>.单控制键+单普通键(例如Ctrl+A) 
//3>.多控制键+单普通键(例如Ctrl+Alt+A) 
//上面的代码中演示了2,其它情况以此类推,无非就是添几个条件再&&起来就好

四.使用全局键盘监听需要注意的问题(请读者朋友务必看看)

1.在应用程序中使用全局键盘监听,会被360发现,弹窗提示用户“有程序正在监听键盘输入,是否阻止?”

所以如果程序中必须要用Hook应该告诉用户不会泄露其信息等等

或者直接把App提交给360审核

否则杀软的提示会对用户体验造成极大的影响

转载于:https://www.cnblogs.com/zhaoyuncai/p/7684958.html

C#全局键盘监听(Hook)的使用(转载)相关推荐

  1. C#全局键盘监听(Hook)

    一.为什么需要全局键盘监听? 在某些情况下应用程序需要实现快捷键执行特定功能,例如大家熟知的QQ截图功能Ctrl+Alt+A快捷键,只要QQ程序在运行(无论是拥有焦点还是处于后台运行状态),都可以按下 ...

  2. python全局键盘监听(pynput快捷键);利用pywin32快速截屏并生成视频

    python全局键盘监听(pynput快捷键):利用pywin32快速截屏并生成视频 第一次在CSDN写博客,有点小紧张(/ω\) 以下内容完全个人理解,有错误请指出~ 最近在用python做一个小工 ...

  3. java实现全局键盘监听

    java实现全局键盘监听 Java本身是无法对桌面进行全局键盘监听的,无法设置全局快捷键,当焦点从java程序面板失去时,自带的监听器就无法监听了,但是比如一些用java写的截图程序是需要全局快捷键操 ...

  4. 一个简单的键盘监听木马dawenxi的制作

    事实上,我最初是没想到这学期的物联网安全课程会以答辩形式作为考核的,更没想到的是老师竟然让我们自己设计一个针对物联网的病毒或者针对物联网的漏洞,还要求不能被查杀.这难度,属实大?_? .可是,我不想挂 ...

  5. 安卓之软键盘监听与切换软键盘状态和重新获取EditText焦点

    最近在工作中的时候遇到了关于使用ScrollView作为外层点击内部EditText弹出软键盘滑动页面的问题,百度了好久各种教程,但是对于我的页面来说没有一个有用的,自己花了1天时间终于搞定了,今天在 ...

  6. 键盘监听木马病毒原理

    前往我的博客以获得更好的阅读体验键盘监听木马病毒原理 - DearXuan的主页https://blog.dearxuan.com/2021/08/29/%E9%94%AE%E7%9B%98%E7%9 ...

  7. android 软键盘监听、隐藏、显示、点击空白处隐藏和Android KEYCODE 键值大全

    android 软键盘监听.隐藏.显示.点击空白处隐藏. textView.setOnKeyListener(new View.OnKeyListener() {@Overridepublic boo ...

  8. java swing button和键盘监听冲突问题

    原因: 点击button会让jframe失去焦点,然后键盘监听不起作用 解决: 让jframe重新获取焦点就行了 jf.setFocusable(true); // JFrame jf = new J ...

  9. qmainwindow键盘监听

    class MyWidget(QtGui.QmainWindow):     keyPressed = QtCore.pyqtSignal() def keyPressEvent(self, even ...

最新文章

  1. HDU4549(矩阵快速幂+快速幂)
  2. 韩顺平循序渐进学java 第10.11讲 继承.重载.覆盖
  3. 【Python学习系列十三】Python机器学习库scikit-learn实现逻辑回归
  4. C语言到嵌入式Linux开发项目指导
  5. 易校网校园综合跑腿小程序源码修复运营版
  6. 进击的AssetBundles和它的工具们
  7. Python ASCII码
  8. 26个最好免费下载电子书(Ebooks)的网站
  9. 【Python游戏】贪吃蛇升级版——双人贪吃蛇小游戏 | 附带源码
  10. 如何快速从入门到精通linux
  11. 机器学习(3)特征提取2 -- 文本特征提取(包括中文文本特征提取)
  12. K线形态识别—K线反转形态之底部反转形态
  13. bilibiliC++25程序流程结构-选择结构-多行ifi语句
  14. 微型计算机增刊2016,科幻世界·2016年增刊
  15. 计算机图形学--动画与模拟
  16. CentOS7更换国内源以及安装补充更新源(安装nethogs)
  17. 关于Vcc和Vdd的区别
  18. CodeForces - 711A 找座位 难度:C++入门 复杂度:简单 翻译难度:难
  19. 【算法leetcode】1557. 可以到达所有点的最少点数目(rust和go我都要)
  20. Android Google地图接入(二)

热门文章

  1. 转】R利剑NoSQL系列文章 之 Hive
  2. OTN交换amp;P-OTN有效减少100G网络成本(一)
  3. Load-time relocation of shared libraries
  4. 印章WinForm自定义控件封装,提供源码下载
  5. resin服务器配置错误
  6. 观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)
  7. Vue封装一个简单轻量的上传文件组件
  8. Android应用安全与防范之签名校验
  9. 发布程序后显示:未能创建类型
  10. ActiveMQ跑起来