知识大全 C#中用钩子(hook)实现的屏幕键盘
Posted 函数
篇首语:千教万教教人求真,千学万学学做真人。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 C#中用钩子(hook)实现的屏幕键盘相关的知识,希望对你有一定的参考价值。
C#中用钩子(hook)实现的屏幕键盘 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
要实现一个屏幕键盘 需要监听所有键盘事件 无论窗体是否被激活 因此需要一个全局的钩子 也就是系统范围的钩子
什么是钩子(Hook)
钩子(Hook)是Windows提供的一种消息处理机制平台 是指在程序正常运行中接受信息之前预先启动的函数 用来检查和修改传给该程序的信息 (钩子)实际上是一个处理消息的程序段 通过系统调用 把它挂入系统 每当特定的消息发出 在没有到达目的窗口前 钩子程序就先捕获该消息 亦即钩子函数先得到控制权 这时钩子函数即可以加工处理(改变)该消息 也可以不作处理而继续传递该消息 还可以强制结束消息的传递 注意 安装钩子函数将会影响系统的性能 监测 系统范围事件 的系统钩子特别明显 因为系统在处理所有的相关事件时都将调用您的钩子函数 这样您的系统将会明显的减慢 所以应谨慎使用 用完后立即卸载 还有 由于您可以预先截获其它进程的消息 所以一旦您的钩子函数出了问题的话必将影响其它的进程
钩子的作用范围
一共有两种范围(类型)的钩子 局部的和远程的 局部钩子仅钩挂自己进程的事件 远程的钩子还可以将钩挂其它进程发生的事件 远程的钩子又有两种 基于线程的钩子将捕获其它进程中某一特定线程的事件 简言之 就是可以用来观察其它进程中的某一特定线程将发生的事件 系统范围的钩子将捕捉系统中所有进程将发生的事件消息
Hook 类型Windows共有 种Hooks 每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制 下面描述所有可以利用的Hook类型的发生时机 详细内容可以查阅MSDN 这里只介绍我们将要用到的两种类型的钩子 ( )WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息
( )WH_MOUSE_LL Hook
WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息
下面的 class 把 API 调用封装起来以便调用
// NativeMethods cs using System; using System Runtime InteropServices; using System Drawing; namespace CnBlogs Youzai ScreenKeyboard [StructLayout(LayoutKind Sequential)] internal struct MOUSEINPUT public int dx; public int dy; public int mouseData; public int dwFlags; public int time; public IntPtr dwExtraInfo; [StructLayout(LayoutKind Sequential)] internal struct KEYBDINPUT public short wVk; public short wScan; public int dwFlags; public int time; public IntPtr dwExtraInfo; [StructLayout(LayoutKind Explicit)] internal struct Input [FieldOffset( )] public int type; [FieldOffset( )] public MOUSEINPUT mi; [FieldOffset( )] public KEYBDINPUT ki; [FieldOffset( )] public HARDWAREINPUT hi; [StructLayout(LayoutKind Sequential)] internal struct HARDWAREINPUT public int uMsg; public short wParamL; public short wParamH; internal class INPUT public const int MOUSE = ; public const int KEYBOARD = ; public const int HARDWARE = ; internal static class NativeMethods [DllImport( User dll CharSet = CharSet Auto SetLastError = false)] internal static extern IntPtr GetWindowLong(IntPtr hWnd int nIndex); [DllImport( User dll CharSet = CharSet Auto SetLastError = false)] internal static extern IntPtr SetWindowLong(IntPtr hWnd int nIndex int dwNewLong); [DllImport( User dll EntryPoint = SendInput CharSet = CharSet Auto)] internal static extern UInt SendInput(UInt nInputs Input[] pInputs Int cbSize); [DllImport( Kernel dll EntryPoint = GetTickCount CharSet = CharSet Auto)] internal static extern int GetTickCount(); [DllImport( User dll EntryPoint = GetKeyState CharSet = CharSet Auto)] internal static extern short GetKeyState(int nVirtKey); [DllImport( User dll EntryPoint = SendMessage CharSet = CharSet Auto)] internal static extern IntPtr SendMessage(IntPtr hWnd int msg IntPtr wParam
IntPtr lParam);
安装钩子
使用SetWindowsHookEx函数(API函数) 指定一个Hook类型 自己的Hook过程是全局还是局部Hook 同时给出Hook过程的进入点 就可以轻松的安装自己的Hook过程 SetWindowsHookEx总是将你的Hook函数放置在Hook链的顶端 你可以使用CallNextHookEx函数将系统消息传递给Hook链中的下一个函数
对于某些类型的Hook 系统将向该类的所有Hook函数发送消息 这时 Hook函数中的CallNextHookEx语句将被忽略 全局(远程钩子)Hook函数可以拦截系统中所有线程的某个特定的消息 为了安装一个全局Hook过程 必须在应用程序外建立一个DLL并将该Hook函数封装到其中 应用程序在安装全局Hook过程时必须先得到该DLL模块的句柄 将Dll名传递给LoadLibrary 函数 就会得到该DLL模块的句柄 得到该句柄 后 使用GetProcAddress函数可以得到Hook过程的地址 最后 使用SetWindowsHookEx将 Hook过程的首址嵌入相应的Hook链中 SetWindowsHookEx传递一个模块句柄 它为Hook过程的进入点
线程标识符置为 该Hook过程同系统中的所有线程关联 如果是安装局部Hook此时该Hook函数可以放置在DLL中 也可以放置在应用程序的模块段 在C#中通过平台调用(前文已经介绍过)来调用API函数
public void Start(bool installMouseHook bool installKeyboardHook) if (hMouseHook == IntPtr Zero && installMouseHook) MouseHookProcedure = new HookProc(MouseHookProc); hMouseHook = SetWindowsHookEx( WH_MOUSE_LL MouseHookProcedure Marshal GetHINSTANCE( Assembly GetExecutingAssembly() GetModules()[ ]) ); if (hMouseHook == IntPtr Zero) int errorCode = Marshal GetLastWin Error(); Stop(true false false); throw new Win Exception(errorCode); if (hKeyboardHook == IntPtr Zero && installKeyboardHook) KeyboardHookProcedure = new HookProc(KeyboardHookProc); //install hook hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL KeyboardHookProcedure Marshal GetHINSTANCE( Assembly GetExecutingAssembly() GetModules()[ ]) ); // If SetWindowsHookEx fails if (hKeyboardHook == IntPtr Zero) // Returns the error code returned by the last // unmanaged function called using platform invoke // that has the DllImportAttribute SetLastError flag set int errorCode = Marshal GetLastWin Error(); //do cleanup Stop(false true false); //Initializes and throws a new instance of the // Win Exception class with the specified error throw new Win Exception(errorCode);
使用完钩子后 要进行卸载 这个可以写在析构函数中
public void Stop() this Stop(true true true); public void Stop(bool uninstallMouseHook bool uninstallKeyboardHook bool throwExceptions) // if mouse hook set and must be uninstalled if (hMouseHook != IntPtr Zero && uninstallMouseHook) // uninstall hook bool retMouse = UnhookWindowsHookEx(hMouseHook); // reset invalid handle hMouseHook = IntPtr Zero; // if failed and exception must be thrown if (retMouse == false && throwExceptions) // Returns the error code returned by the last unmanaged function // called using platform invoke that has the DllImportAttribute // SetLastError flag set int errorCode = Marshal GetLastWin Error(); // Initializes and throws a new instance of the Win Exception class // with the specified error throw new Win Exception(errorCode); // if keyboard hook set and must be uninstalled if (hKeyboardHook != IntPtr Zero && uninstallKeyboardHook) // uninstall hook bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook); // reset invalid handle hKeyboardHook = IntPtr Zero; // if failed and exception must be thrown if (retKeyboard == false && throwExceptions) // Returns the error code returned by the last unmanaged function // called using platform invoke that has the DllImportAttribute // SetLastError flag set int errorCode = Marshal GetLastWin Error(); // Initializes and throws a new instance of the Win Exception class // with the specified error throw new Win Exception(errorCode);
将这个文件编译成一个dll 即可在应用程序中调用 通过它提供的事件 便可监听所有的键盘事件 但是 这只能监听键盘事件 没有键盘的情况下 怎么会有键盘事件?其实很简单 通过SendInput API函数提供虚拟键盘代码的调用即可模拟键盘输入 下面的代码模拟一个 KeyDown 和 KeyUp 过程 把他们连接起来就是一次按键过程
private void SendKeyDown(short key) Input[] input = new Input[ ]; input[ ] type = INPUT KEYBOARD; input[ ] ki wVk = key; input[ ] ki time = NativeMethods GetTickCount(); if (NativeMethods SendInput((uint)input Length input Marshal SizeOf(input[ ])) < input Length) throw new Win Exception(Marshal GetLastWin Error()); private void SendKeyUp(short key) Input[] input = new Input[ ]; input[ ] type = INPUT KEYBOARD; input[ ] ki wVk = key; input[ ] ki dwFlags = KeyboardConstaint KEYEVENTF_KEYUP; input[ ] ki time = NativeMethods GetTickCount(); if (NativeMethods SendInput((uint)input Length input Marshal SizeOf(input[ ])) < input Length) throw new Win Exception(Marshal GetLastWin Error());
自己实现一个 KeyBoardButton 控件用作按钮 用 Visual Studio 或者 SharpDevelop 为屏幕键盘设计 UI 然后在这些 Button 的 Click 事件里面模拟一个按键过程
private void ButtonOnClick(object sender EventArgs e) KeyboardButton btnKey = sender as KeyboardButton; if (btnKey == null) return; SendKeyCommand(btnKey); private void SendKeyCommand(KeyboardButton keyButton) short key = keyButton VKCode; if (binationVKButtonsMap ContainsKey(key)) if (keyButton Checked) SendKeyUp(key); else SendKeyDown(key); else SendKeyDown(key); SendKeyUp(key);
其中 binationVKButtonsMap 是一个 IDictionary
还有一些其他的常量,把它写在一个静态 class 里吧。
1 // KeyboardConstaint.cs2 internal static class KeyboardConstaint 3 internal static readonly short VK_F1 = 0x70;4 internal static readonly short VK_F2 = 0x71;5 internal static readonly short VK_F3 = 0x72;6 internal static readonly short VK_F4 = 0x73;7 internal static readonly short VK_F5 = 0x74;8 internal static readonly short VK_F6 = 0x75;9 internal static readonly short VK_F7 = 0x76;10 internal static readonly short VK_F8 = 0x77;11 internal static readonly short VK_F9 = 0x78;12 internal static readonly short VK_F10 = 0x79;13 internal static readonly short VK_F11 = 0x7A;14 internal static readonly short VK_F12 = 0x7B;1516 internal static readonly short VK_LEFT = 0x25;17 internal static readonly short VK_UP = 0x26;18 internal static readonly short VK_RIGHT = 0x27;19 internal static readonly short VK_DOWN = 0x28;2021 internal static readonly short VK_NONE = 0x00;22 internal static readonly short VK_ESCAPE = 0x1B;23 internal static readonly short VK_EXECUTE = 0x2B;24 internal static readonly short VK_CANCEL = 0x03;25 internal static readonly short VK_RETURN = 0x0D;26 internal static readonly short VK_ACCEPT = 0x1E;27 internal static readonly short VK_BACK = 0x08;28 internal static readonly short VK_TAB = 0x09;29 internal static readonly short VK_DELETE = 0x2E;30 internal static readonly short VK_CAPITAL = 0x14;31 internal static readonly short VK_NUMLOCK = 0x90;32 internal static readonly short VK_SPACE = 0x20;33 internal static readonly short VK_DECIMAL = 0x6E;34 internal static readonly short VK_SUBTRACT = 0x6D;3536 internal static readonly short VK_ADD = 0x6B;37 internal static readonly short VK_DIVIDE = 0x6F;38 internal static readonly short VK_MULTIPLY = 0x6A;39 internal static readonly short VK_INSERT = 0x2D;4041 internal static readonly short VK_OEM_1 = 0xBA; // \';:\' for US42 internal static readonly short VK_OEM_PLUS = 0xBB; // \'+\' any country4344 internal static readonly short VK_OEM_MINUS = 0xBD; // \'-\' any country4546 internal static readonly short VK_OEM_2 = 0xBF; // \'/?\' for US47 internal static readonly short VK_OEM_3 = 0xC0; // \'`~\' for US48 internal static readonly short VK_OEM_4 = 0xDB; // \'[\' for US49 internal static readonly short VK_OEM_5 = 0xDC; // \'\\|\' for US50 internal static readonly short VK_OEM_6 = 0xDD; // \']\' for US51 internal static readonly short VK_OEM_7 = 0xDE; // \'\'"\' for US52 internal static readonly short VK_OEM_PERIOD = 0xBE; // \'.>\' any country53 internal static readonly short VK_OEM_MA = 0xBC; // \',<\' any country54 internal static readonly short VK_SHIFT = 0x10;55 internal static readonly short VK_CONTROL = 0x11;56 internal static readonly short VK_MENU = 0x12;57 internal static readonly short VK_LWIN = 0x5B;58 internal static readonly short VK_RWIN = 0x5C;59 internal static readonly short VK_APPS = 0x5D;6061 internal static readonly short VK_LSHIFT = 0xA0;62 internal static readonly short VK_RSHIFT = 0xA1;63 internal static readonly short VK_LCONTROL = 0xA2;64 internal static readonly short VK_RCONTROL = 0xA3;65 internal static readonly short VK_LMENU = 0xA4;66 internal static readonly short VK_RMENU = 0xA5;6768 internal static readonly short VK_SNAPSHOT = 0x2C;69 internal static readonly short VK_SCROLL = 0x91;70 internal static readonly short VK_PAUSE = 0x13;71 internal static readonly short VK_HOME = 0x24;7273 internal static readonly short VK_NEXT = 0x22;74 internal static readonly short VK_PRIOR = 0x21;75 internal static readonly short VK_END = 0x23;7677 internal static readonly short VK_NUMPAD0 = 0x60;78 internal static readonly short VK_NUMPAD1 = 0x61;79 internal static readonly short VK_NUMPAD2 = 0x62;80 internal static readonly short VK_NUMPAD3 = 0x63;81 internal static readonly short VK_NUMPAD4 = 0x64;82 internal static readonly short VK_NUMPAD5 = 0x65;83 internal static readonly short VK_NUMPAD5NOTHING = 0x0C;84 internal static readonly short VK_NUMPAD6 = 0x66;85 internal static readonly short VK_NUMPAD7 = 0x67;86 internal static readonly short VK_NUMPAD8 = 0x68;87 internal static readonly short VK_NUMPAD9 = 0x69;8889 internal static readonly short KEYEVENTF_EXTENDEDKEY = 0x0001;90 internal static readonly short KEYEVENTF_KEYUP = 0x0002;9192 internal static readonly int GWL_EXSTYLE = -20;93 internal static readonly int WS_DISABLED = 0X8000000;94 internal static readonly int WM_SETFOCUS = 0X0007;95
屏幕键盘必须是一个不能获得输入焦点的窗体,在这个窗体的构造函数里,可以安装一个全局鼠标钩子,再通过调用 SetWindowLong API 函数完成。
1UserActivityHook hook = new UserActivityHook(true, true);2hook.MouseActivity += HookOnMouseActivity;34private void HookOnMouseActivity(object sener, HookEx.MouseExEventArgs e) 5 Point location = e.Location;67 if (e.Button == MouseButtons.Left) 8 Rectangle captionRect = new Rectangle(this.Location, new Size(this.Width, 9 SystemInformation.CaptionHeight));10 if (captionRect.Contains(location)) 11 NativeMethods.SetWindowLong(this.Handle, KeyboardConstaint.GWL_EXSTYLE,12 (int)NativeMethods.GetWindowLong(this.Handle, KeyboardConstaint.GWL_EXSTYLE)13 & (~KeyboardConstaint.WS_DISABLED));14 NativeMethods.SendMessage(this.Handle, KeyboardConstaint.WM_SETFOCUS, IntPtr.Zero,
IntPtr.Zero);15 else 16 NativeMethods.SetWindowLong(this.Handle, KeyboardConstaint.GWL_EXSTYLE,17 (int)NativeMethods.GetWindowLong(this.Handle, KeyboardConstaint.GWL_EXSTYLE) | 18 KeyboardConstaint.WS_DISABLED);19 20 21
鼠标单击标题栏,让屏幕键盘可以接收焦点,并激活,单击其他部分则不激活窗体(如果激活了,其他程序必然取消激活,输入就无法进行了),这样才可以进行输入,并且保证了可以拖动窗体到其他位置。
至此,一个屏幕键盘程序差不多完成了,能够实现与实际键盘完全同步。至于窗体,按键重绘,以及 Num Lock, Caps Lock, Scroll Lock 等键盘灯的模拟,这里就不讲了,如果有兴趣,可以下载完整的代码。
说明:本程序参考了 Jeffrey Richter 先生的著作 CLR via C#, Second Edition, MSDN 以及一些网络资料。
cha138/Article/program/net/201311/11360相关参考
纯C#钩子实现及应用 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 要实现系统钩子其实很简单调用
这个问题来自论坛提问咨询如何通过设置钩子监视鼠标的移动C#的大致代码如下 usingSystem;usingSystemWindowsForms;usingSystemRuntimeInte
C#中使用钩子控制鼠标实现代码 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 工作中有这
C#实现木马-键盘记录器 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 这是RegistryRe
从键盘布局开始说C#中的符号 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 前言 年关将至因此
VS2005中C#用代码打开软键盘 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nb
用C#实现全屏幕截图 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 今天一位同事想写一个全屏幕截
C#获取屏幕分辨率 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! usingSystemWind
将开关量操作输出信号直接引到该操作对象反馈信号输入端,记录操作员站键盘指令发出,到屏幕显示反馈信号的时间、重复10次取平
将开关量操作输出信号直接引到该操作对象反馈信号输入端,记录操作员站键盘指令发出,到屏幕显示反馈信号的时间、重复10次取平均值,规程中称该值为()A、开关量信号系统响应时间B、开关量信号系统响应时间的平
将开关量操作输出信号直接引到该操作对象反馈信号输入端,记录操作员站键盘指令发出,到屏幕显示反馈信号的时间、重复10次取平
将开关量操作输出信号直接引到该操作对象反馈信号输入端,记录操作员站键盘指令发出,到屏幕显示反馈信号的时间、重复10次取平均值,规程中称该值为()A、开关量信号系统响应时间B、开关量信号系统响应时间的平