本文由xyz8808贡献
2009-11-26 12:49//定义变量
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0;
HookProc KeyboardHookProcedure;
/*************************
* 声明API函数
* ***********************/
// 安装钩子 (using System.Runtime.InteropServices;)
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int SetWindowsHookEx(int idHook,HookProc lpfn, IntPtr hInstance, int threadId);
// 卸载钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
// 继续下一个钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得当前线程编号(线程钩子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
//钩子子程:就是钩子所要做的事情
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
{
/****************
//线程键盘钩子判断是否按下键
Keys keyData = (Keys)wParam;
if(lParam.ToInt32() > 0)
{
// 键盘按下
}
if(lParam.ToInt32() < 0)
{
// 键盘抬起
}
****************/
/****************
//全局键盘钩子判断是否按下键
wParam = = 0x100 // 键盘按下
wParam = = 0x101 // 键盘抬起
****************/
KeyMSG m = (KeyMSG) Marshal.PtrToStructure(lParam, typeof(KeyMSG));//键盘
// 在这里添加你想要做是事情(比如把键盘nCode记录下来,搞个邮件发送程序发到自己的邮箱去)
return 0;//如果返回1,则结束消息,这个消息到此为止,不再传递。如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
//键盘结构
public struct KeyMSG
{
public int vkCode; //键值
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
// 安装钩子
public void HookStart()
{
if(hKeyboardHook == 0)
{
// 创建HookProc实例
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
// 设置线程钩子
hKeyboardHook = SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//************************************
//键盘线程钩子
//SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId()); //GetCurrentThreadId()为要监视的线程ID,你完全可以自己写个方法获取QQ的线程哦
//键盘全局钩子,需要引用空间(using System.Reflection;)
//SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//
//关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函数将钩子加入到钩子链表中,说明一下四个参数:
//idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,
//线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。
//
//lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可
//以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
//
//hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子程代码位于当前
//进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。
//
//threadedId 与安装的钩子子程相关联的线程的标识符。如果为0,钩子子程与所有的线程关联,即为全局钩子。
//************************************
// 如果设置钩子失败
if(hKeyboardHook == 0 )
{
HookStop();
throw new Exception("SetWindowsHookEx failed.");
}
}
}
// 卸载钩子
public void HookStop()
{
bool retKeyboard = true;
if(hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!( retKeyboard))
throw new Exception("UnhookWindowsHookEx failed.");
}
//*****************************
试用了一下,不是很稳定,改回自带的 KeyUp函数了,记录以备查
//自定义类型
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
//接口调用
[DllImport( "User32.DLL ")]
public static extern int SendMessage(IntPtr hWnd, uint Msg,int wParam,int lParam);
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport("User32.Dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport( "user32",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(HookType idHook,HOOKPROC lpfn,int hmod,int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,int wParam,int lParam);
//函数实现
public void SetHook()
{
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
IntPtr ParenthWnd = new IntPtr(0);
bool isPressed = (((lParam & 0x80000000) == 0) && (nCode==0));
CallNextHookEx(m_HookHandle, nCode, wParam, lParam); //先把消息发给系统默认处理队列
global.lastKeyValue = wParam;
ParenthWnd = FindWindow( null,"零售业务");
switch(wParam)
{
case 112: //修改数量 F1
if(isPressed)
{
inputType = global.ST_NUM_STP1;
doKeyWork();
}
break;
case 113: //备注 F2
if(isPressed)
{
inputType = global.ST_REMARK;
textBoxBZ.Focus();
textBoxBZ.SelectAll();
}
break;
case 114: //摘要 F3
if(isPressed)
{
inputType = global.ST_ABST;
textBoxZY.Focus();
textBoxZY.SelectAll();
}
break;
case 115: //支付 F4
if(isPressed)
if((global.saleList == null) || (global.saleList.Length == 0))
break;
else
{
showProductInfo();
labelCPZS.Text = global.sAllNum.ToString() + " 件";
labelYSJE.Text = global.sAllValue.ToString() + " 元";
labelYHJE.Text = global.sPriValue.ToString() + " 元";
labelSSJE.Text = global.sDueValue.ToString() + " 元";
inputType = global.ST_GATHER_STP1;
textBoxSK.Focus();
textBoxSK.SelectAll();
}
break;
case 116: //输入产品 F5
if(isPressed)
{
inputType = global.ST_PRODUCT;
textBoxSPMC.Focus();
textBoxSPMC.SelectAll();
}
break;
case 117: //开始新订单 F6
if(isPressed)
{
resetSale(); //销售界面复位
}
break;
default:
//CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
break;
}
return(0);
}
///
/// 清理所有正在使用的资源。
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
C#中键盘钩子的使用
默认分类 2008-07-05 17:33 阅读53 评论0 字号: 大大 中中 小小 public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在这里放置你的处理代码 return 0;
}
}
使用方法
可以在Form的构造函数里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();
C#+低级Windows API钩子拦截键盘输入
www.diybl.com 时间:2010-01-14 作者:匿名 编辑:Smi1e 点击: 9 [评论]
-
-
摘要 在家里,婴儿和其它动物可能会重击你的计算机键盘,致使出现各种无法预言的结果。本文中的这个C#示例应用程序将向你展示如何基于Windows钩子API来实现在击键造成任何危害之前捕获它们。
一. 简介
猫和婴儿有很多共同之处。他们都喜欢吃家中养植的植物,都非常讨厌关门。他们也都爱玩弄你的键盘,结果是,你正发送给你的老板的电子邮件可能是以半截句子发送出去的,你的Excel帐户也被加入了一些乱七八糟的内容,并且你还没有注意到,当打开Windows资源管理器时,若干文件已经被移到了回收站!
其解决方案是,开发一个应用程序实现如下功能:只要键盘处于"威胁状态"你就可以进行切换,并确保任何键盘输入活动都不会造成危害。本文想展示如何使用一种低级Windows API钩子在一个C#应用程序中实现键盘"控制"。下图是本文示例程序的一个运行快照。
二. 背景
其实,已经存在许多有关于Windows钩子的文章和示例代码,并且已经有人编写过与本文几乎一样的C++示例程序。然而,当我搜索相应的C#应用程序的源码时,却找到极少的.NET示例,而且没有一个程序能够提供一个方便的自包含的C#类。
.NET框架能够使你以托管方式来存取你最常使用的键盘事件(通过KeyPress,KeyUp和KeyDown)。遗憾的是,这些事件都不能被用来停止Windows组合键(如Alt+Tab或Windows"开始"键),从而允许用户"远离"某一个应用程序。
本文的想法在操作系统级上捕获键盘事件而不是通过框架级来实现。为此,应用程序需要使用Windows API函数来把它自身添加到应用程序"钩子链"中以监听来自操作系统的键盘消息。当它收到这种类型的消息时,该应用程序能够选择性地传递消息,或者进行正常处理,或者"镇压"它以便不再有其它应用程序(包括Windows)来影响它。本文正是想解释其实现机理。
然而,请注意,本文中的代码仅适用于基于NT版本的Windows(NT,2000和XP),并且无法使用这个方法来停用Ctrl+Alt+Delete。有关于如何实现这一点,你可以参考MSDN有关资料。
三. 使用代码
为了易于使用,我在本文中提供了两个独立的zip文件。一个仅包含KeyboardHook类,这是本文介绍的重点。另一个是一个完整的微软Visual C# 2005 Express Edition应用程序工程,名叫"Baby Keyboard Bash",它实现显示击键的名字或彩色的形状以响应于击键。
四. 实例化类
键盘钩子是通过keyboard.cs中的KeyboardHook类来建立和管理的。这个类实现了IDisposable接口,因此,实例化它的最简单的方法是在应用程序的Main()方法中使用using关键字来封装Application.Run()调用。这将确保只要该应用程序开始即建立钩子并且,更重要的是,当该应用程序结束时立即使这个钩子失效。
这个类引发一个事件来警告应用程序已经有键被按下,因此主表单能够存取在Main()方法中创建的KeyboardHook实例就显得非常重要;最简单的方法是把这个实例存储在一个公共成员变量中。
KeyboardHook提供了三种构造器来启用或禁用某些设置:
· KeyboardHook():捕获所有击键,没有任何内容传递到Windows或另外的应用程序。
· KeyboardHook(string param):把参数串转换为Parameters枚举中的值之一,然后调用下面的构造器:
· KeyboardHook(KeyboardHook.Parameters enum):根据从Parameters枚举中选择的值的不同,分别启动下列设置:
o Parameters.AllowAltTab:允许用户使用Alt+Tab切换到另外的应用程序。
o Parameters.AllowWindowsKey:允许用户使用Ctrl+Esc或一种Windows键存取任务栏和开始菜单。
o Parameters.AllowAltTabAndWindows:启用Alt+Tab,Ctrl+Esc和Windows键。
o Parameters.PassAllKeysToNextApp:如果该参数为true,那么所有的击键将被传递给任何其它监听应用程序(包括Windows)。
当击键继续被键盘钩子捕获时,启用Alt+Tab和/或Windows键允许实际使用该计算机者切换到另一个应用程序并且使用鼠标与之交互。PassAllKeysToNextApp设置有效地禁用了击键捕获;这个类也是建立一个低级键盘钩子并且引发它的KeyIntercepted事件,但是它还负责把键盘事件传递到另一个监听程序。
因此,实例化该类以捕获所有击键的方法如下:
public static KeyboardHook kh;
[STAThread]
static void Main()
{
//其它代码
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}
五. 处理KeyIntercepted事件
当一外键被按下时,这个KeyboardHook类激活一个包含一些KeyboardHookEventArgs的KeyIntercepted事件。这是通过一个KeyboardHookEventHandler类型的方法使用以下方式来实现的:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
这个KeyboardHookEventArgs返回关于被按下键的下列信息:
· KeyName:键名,通过把捕获的键代码强制转换为System.Windows.Forms.Keys而获得。
· KeyCode:由键盘钩子返回的原来的键代码
· PassThrough:指出是否这个KeyboardHook实例被配置以允许该击键传递到其它应用程序。如果你想允许一用户使用Alt+Tab或 Ctrl+Esc/Windows键切换到其它的应用程序的话,那么对之进行检查是很有用的。
然后,使用一个具有适当签名的方法来执行击键所调用的任何任务。下面是一个示例片断:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
//检查是否这个键击事件被传递到其它应用程序并且停用TopMost,以防他们需要调到前端
if (e.PassThrough)
{
this.TopMost = false;
}
ds.Draw(e.KeyName);
}
本文的剩下部分将解释低级键盘钩子是如何在KeyboardHook中实现的。
六. 实现一个低级Windows API键盘钩子
在user32.dll中,Windows API包含三个方法来实现此目的:
· SetWindowsHookEx,它负责建立键盘钩子
· UnhookWindowsHookEx,它负责移去键盘钩子
· CallNextHookEx,它负责把击键信息传递到下一个监听键盘事件的应用程序
创建一个能够拦截键盘的应用程序的关键是,实现前面两个方法,而"放弃"第三个。结果是,任何击键都只能传递到这个应用程序中。
为了实现这一目标,第一步是包括System.Runtime.InteropServices命名空间并且导入API方法,首先是SetWindowsHookEx:
using System.Runtime.InteropServices
……
//在类内部:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
导入UnhookWindowsHookEx和CallNextHookEx的代码请见后面的讨论。
下一步是调用SetWindowsHookEx来建立钩子,这时需要传递下列四个参数:
· idHook:
这个数字决定了要建立的钩子的类型。例如,SetWindowsHookEx可以被用于钩住鼠标事件(当然还有其它事件)。在本文情况下,我们仅对13有兴趣,这是键盘钩子的id。为了使代码更易读些,我们把它赋值给一个常数WH_KEYBOARD_LL。
· Lpfn:
这是一个指向函数的长指针,该函数将负责处理键盘事件。在C#中,"指针"是通过传递一个代理类型的实例而获得的,从而使之引用一个适当的方法。这是我们在每次使用钩子时所调用的方法。
这里值得注意的是,这个代理实例需要被存储于这个类的一个成员变量中。这是为了防止一旦第一个方法调用结束它会被作为垃圾回收。
· hMod:
建立钩子的应用程序的一个实例句柄。我找到的绝大多数实例仅把它设置为IntPtr.Zero,理由是不大可能存在该应用程序的多个实例。然而,这部分代码使用了来自于kernel32.dll的GetModuleHandle来标识准确的实例从而使这个类更具灵活性。
· dwThreadId:
当前进程的id。把它设置为0可以使这个钩子成为全局构子,这是相应于一个低级键盘钩子的正确设置。
SetWindowsHookEx返回一个钩子id,这个id将被用于当应用程序结束时从钩子链中脱钩,因此它需要存储在一个成员变量中以备将来使用。KeyboardHook类中的相关代码如下:
private HookHandlerDelegate proc;
private IntPtr hookID = IntPtr.Zero;
private const int WH_KEYBOARD_LL = 13;
public KeyboardHook()
{
proc = new HookHandlerDelegate(HookCallback);
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
hookID = SetWindowsHookEx(WH_KEYBOARD_LL, proc,GetModuleHandle(curModule.ModuleName), 0);
}
}
七. 处理键盘事件
如前面所提及,SetWindowsHookEx需要一个到被用来处理键盘事件的回调函数的指针。它期望有一个使用如下签名的函数:
LRESULT CALLBACK LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam);
其实,建立一个函数指针的C#方法使用了一个代理,因此,向SetWindowsHookEx指出它需要的内容的第一步是使用正确的签名来声明一个代理:
private delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
然后,使用相同的签名编写一个回调方法;这个方法将包含实际上处理键盘事件的所有代码。在KeyboardHook的情况下,它检查是否击键应该被传递给其它应用程序并且接下来激发KeyIntercepted事件。下面是一个简化版本的不带有击键处理代码的情况:
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
//仅为KeyDown事件过滤wParam,否则该代码将再次执行-对于每一次击键(也就是,相应于KeyDown和KeyUp)
//WM_SYSKEYDOWN是捕获Alt相关组合键所必需的
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
//激发事件
OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
//返回一个"哑"值以捕获击键
return (System.IntPtr)1;
}
//事件没有被处理,把它传递给下一个应用程序
return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
接下来,一个到HookCallback的参考被指派给HookHandlerDelegate的一个实例并且被传递到SetWindowsHookEx的调用,正如前一节所展示的。
无论何时一个键盘事件发生,下列参数将被传递给HookCallBack:
· nCode:
根据MSDN文档,回调函数应该返回CallNextHookEx的结果,如果这个值小于零的话。正常的键盘事件将返回一个大于或等于零的nCode值。
· wParam:
这个值指示发生了什么类型的事件:键被按下还是松开,以及是否按下的键是一个系统键(左边或右边的Alt键)。
· lParam:
这是一个存储精确击键信息的结构,例如被按键的代码。在KeyboardHook中声明的这个结构如下:
private struct KBDLLHOOKSTRUCT
{
public int vkCode;
int scanCode;
public int flags;
int time;
int dwExtraInfo;
}
其中的这两个公共参数是在KeyboardHook中的回调方法所使用的仅有的两个参数。vkCoke返回虚拟键代码,它能够被强制转换为System.Windows.Forms.Keys以获得键名,而flags显示是否这是一个扩展键(例如,Windows Start键)或是否同时按下了Alt键。有关于Hook回调方法的完整代码展示在每一种情况下要检查哪些flags值。
如果flags提供的信息和KBDLLHOOKSTRUCT的其它组成元素不需要,那么这个回调方法和代码的签名可以按如下进行修改:
private delegate IntPtr HookHandlerDelegate(
int nCode, IntPtr wParam, IntPtr lParam);
在这种情况中,lParam将仅返回vkCode。
八. 把击键传递到下一个应用程序
一个良好的键盘钩子回调方法应该以调用CallNextHookEx函数并且返回它的结果结束。这可以确保其它应用程序能够有机会处理针对于它们的击键。
然而,KeyboardHook类的主要功能在于,阻止击键被传播到任何其它更多的应用程序。因此它无论在何时处理一次击键,HookCallback都将返回一个哑值:
return (System.IntPtr)1;
另一方面,它确实调用CallNextHookEx-如果它不处理该事件,或如果重载的构造器中的使用KeyboardHook传递的参数允许某些组合键通过。
CallNextHookEx被启用-通过从user32.dll导入该函数,如下列代码所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KeyInfoStruct lParam);
然后,被导入的方法被HookCallMethod所调用,这可以确保所有的通过钩子接收到的参数被继续传递到下一个应用程序中:
CallNextHookEx(hookID, nCode, wParam, ref lParam);
如前面所提及,如果在lParam中的flags是不相关的,那么可以修改导入的CallNextHookEx的签名以把lParam定义为System.IntPtr。
九. 移去钩子
处理钩子的最后一步是使用从user32.dll中导入的UnhookWindowsHookEx函数移去它(当破坏KeyboardHook类的实例时),如下所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
既然KeyboardHook实现IDisposable,那么这可以在Dispose方法中完成。
public void Dispose()
{
UnhookWindowsHookEx(hookID);
}
hookID是构造器在调用SetWindowsHookEx所返回的id。这将从钩子链中删除应用程序。
c#键盘钩子
作者: lzh 类别: C#/VB 日期: 2003-5-27 22:22:19
You can use system hook to hook any messages send to the application.
Hooking the keyboard (WH_KEYBOARD) and mouse (WH_MOUSE) messages can be
used to determine if the user interactive with the computer. You also can
hook all the messages (WH_GETMESSAGE). The hook can be set with this code:
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_GETMESSAGE = 3
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_GETMESSAGE,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//Perform your process
return 0;
}
}
Then you can install the hook procedure by the following code:
Win32Hook hook = new Win32Hook();
hook.SetHook();
用钩子(hook)实现C#的屏幕键盘效果
-
SVN技术网 www.svn8.com 2010-01-20 08:38:02 来源:bbs.svn8.com 作者:佚名 点击:158次
文章摘要:用钩子(hook)实现C#的屏幕键盘效果 要实现一个屏幕键盘,需要监听所有键盘事件,无论窗体是否被激活。因此需要一个全局的钩子,也就 是系统范围的钩子。 什么是钩子(Hook) 钩子(Hook)是Windows提供的一种消息处理机制平台,是指在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)
-
使用完钩子后,要进行卸载,这个可以写在析构函数中。
12 public void Stop() {3 this.Stop(true, true, true);4 }5 6 public void Stop(bool uninstallMouseHook, bool uninstallKeyboardHook, 7 bool throwExceptions) {8 // if mouse hook set and must be uninstalled9 if (hMouseHook != IntPtr.Zero && uninstallMouseHook) {10 // uninstall hook11 bool retMouse = UnhookWindowsHookEx(hMouseHook);12 // reset invalid handle13 hMouseHook = IntPtr.Zero;14 // if failed and exception must be thrown15 if (retMouse == false && throwExceptions) {16 // Returns the error code returned by the last unmanaged function 17 // called using platform invoke that has the DllImportAttribute.18 // SetLastError flag set. 19 int errorCode = Marshal.GetLastWin32Error();20 // Initializes and throws a new instance of the Win32Exception class 21 // with the specified error. 22 throw new Win32Exception(errorCode);23 }24 }2526 // if keyboard hook set and must be uninstalled27 if (hKeyboardHook != IntPtr.Zero && uninstallKeyboardHook) {28 // uninstall hook29 bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook);30 // reset invalid handle31 hKeyboardHook = IntPtr.Zero;32 // if failed and exception must be thrown33 if (retKeyboard == false && throwExceptions) {34 // Returns the error code returned by the last unmanaged function 35 // called using platform invoke that has the DllImportAttribute.36 // SetLastError flag set. 37 int errorCode = Marshal.GetLastWin32Error();38 // Initializes and throws a new instance of the Win32Exception class 39 // with the specified error. 40 throw new Win32Exception(errorCode);41 }42 }43 }44
将这个文件编译成一个dll,即可在应用程序中调用。通过它提供的事件,便可监听所有的键盘事件。
但是,这只能监听键盘事件,没有键盘的情况下,怎么会有键盘事件?其实很简单,通过SendInput
API函数提供虚拟键盘代码的调用即可模拟键盘输入。下面的代码模拟一个 KeyDown 和 KeyUp 过程,
把他们连接起来就是一次按键过程。
1 private void SendKeyDown(short key) {2 Input[] input = new Input[1];3 input[0].type = INPUT.KEYBOARD;4 input[0].ki.wVk = key;5 input[0].ki.time = NativeMethods.GetTickCount();67 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0])) 8 < input.Length) {9 throw new Win32Exception(Marshal.GetLastWin32Error());10 }11 }1213 private void SendKeyUp(short key) {14 Input[] input = new Input[1];15 input[0].type = INPUT.KEYBOARD;16 input[0].ki.wVk = key;17 input[0].ki.dwFlags = KeyboardConstaint.KEYEVENTF_KEYUP;18 input[0].ki.time = NativeMethods.GetTickCount();1920 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0]))21 < input.Length) {22 throw new Win32Exception(Marshal.GetLastWin32Error());23 }24 }
自己实现一个 KeyBoardButton 控件用作按钮,用 Visual Studio 或者 SharpDevelop 为屏幕键盘设计 UI,然后
在这些 Button 的 Click 事件里面模拟一个按键过程。
12 private void ButtonOnClick(object sender, EventArgs e) {3 KeyboardButton btnKey = sender as KeyboardButton;4 if (btnKey == null) {5 return;6 }78 SendKeyCommand(btnKey);9 }10 11 private void SendKeyCommand(KeyboardButton keyButton) {12 short key = keyButton.VKCode;13 if (combinationVKButtonsMap.ContainsKey(key)) {14 if (keyButton.Checked) {15 SendKeyUp(key);16 } else {17 SendKeyDown(key);18 }19 } else {20 SendKeyDown(key);21 SendKeyUp(key);22 }23 }
其中 combinationVKButtonsMap 是一个 IDictionary>, key 存储的是VK_SHIFT, VK_CONTROL 等组合键的键盘码。左右两个按钮对应同一个键盘码,因此需要放在一个 List 里。
标准键盘上的每一个键都有虚拟键码( VK_CODE)与之对应。还有一些其他的常量,
把它写在一个静态 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_COMMA = 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 }
文章来自[SVN中文技术网]转发请保留本站地址:http://www.svn8.com/dotnet/Csharp/2010012018328_2.html
C#中键盘钩子的使用(转)
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在这里放置你的处理代码 return 0;
}
}
使用方法
可以在Form的构造函数里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();
2009-11-26 12:49//定义变量
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0;
HookProc KeyboardHookProcedure;
/*************************
* 声明API函数
* ***********************/
// 安装钩子 (using System.Runtime.InteropServices;)
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int SetWindowsHookEx(int idHook,HookProc lpfn, IntPtr hInstance, int threadId);
// 卸载钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
// 继续下一个钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得当前线程编号(线程钩子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
//钩子子程:就是钩子所要做的事情
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
{
/****************
//线程键盘钩子判断是否按下键
Keys keyData = (Keys)wParam;
if(lParam.ToInt32() > 0)
{
// 键盘按下
}
if(lParam.ToInt32() < 0)
{
// 键盘抬起
}
****************/
/****************
//全局键盘钩子判断是否按下键
wParam = = 0x100 // 键盘按下
wParam = = 0x101 // 键盘抬起
****************/
KeyMSG m = (KeyMSG) Marshal.PtrToStructure(lParam, typeof(KeyMSG));//键盘
// 在这里添加你想要做是事情(比如把键盘nCode记录下来,搞个邮件发送程序发到自己的邮箱去)
return 0;//如果返回1,则结束消息,这个消息到此为止,不再传递。如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
//键盘结构
public struct KeyMSG
{
public int vkCode; //键值
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
// 安装钩子
public void HookStart()
{
if(hKeyboardHook == 0)
{
// 创建HookProc实例
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
// 设置线程钩子
hKeyboardHook = SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//************************************
//键盘线程钩子
//SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId()); //GetCurrentThreadId()为要监视的线程ID,你完全可以自己写个方法获取QQ的线程哦
//键盘全局钩子,需要引用空间(using System.Reflection;)
//SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//
//关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函数将钩子加入到钩子链表中,说明一下四个参数:
//idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,
//线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。
//
//lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可
//以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
//
//hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子程代码位于当前
//进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。
//
//threadedId 与安装的钩子子程相关联的线程的标识符。如果为0,钩子子程与所有的线程关联,即为全局钩子。
//************************************
// 如果设置钩子失败
if(hKeyboardHook == 0 )
{
HookStop();
throw new Exception("SetWindowsHookEx failed.");
}
}
}
// 卸载钩子
public void HookStop()
{
bool retKeyboard = true;
if(hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!( retKeyboard))
throw new Exception("UnhookWindowsHookEx failed.");
}
//*****************************
试用了一下,不是很稳定,改回自带的 KeyUp函数了,记录以备查
//自定义类型
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
//接口调用
[DllImport( "User32.DLL ")]
public static extern int SendMessage(IntPtr hWnd, uint Msg,int wParam,int lParam);
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport("User32.Dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport( "user32",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(HookType idHook,HOOKPROC lpfn,int hmod,int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,int wParam,int lParam);
//函数实现
public void SetHook()
{
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
IntPtr ParenthWnd = new IntPtr(0);
bool isPressed = (((lParam & 0x80000000) == 0) && (nCode==0));
CallNextHookEx(m_HookHandle, nCode, wParam, lParam); //先把消息发给系统默认处理队列
global.lastKeyValue = wParam;
ParenthWnd = FindWindow( null,"零售业务");
switch(wParam)
{
case 112: //修改数量 F1
if(isPressed)
{
inputType = global.ST_NUM_STP1;
doKeyWork();
}
break;
case 113: //备注 F2
if(isPressed)
{
inputType = global.ST_REMARK;
textBoxBZ.Focus();
textBoxBZ.SelectAll();
}
break;
case 114: //摘要 F3
if(isPressed)
{
inputType = global.ST_ABST;
textBoxZY.Focus();
textBoxZY.SelectAll();
}
break;
case 115: //支付 F4
if(isPressed)
if((global.saleList == null) || (global.saleList.Length == 0))
break;
else
{
showProductInfo();
labelCPZS.Text = global.sAllNum.ToString() + " 件";
labelYSJE.Text = global.sAllValue.ToString() + " 元";
labelYHJE.Text = global.sPriValue.ToString() + " 元";
labelSSJE.Text = global.sDueValue.ToString() + " 元";
inputType = global.ST_GATHER_STP1;
textBoxSK.Focus();
textBoxSK.SelectAll();
}
break;
case 116: //输入产品 F5
if(isPressed)
{
inputType = global.ST_PRODUCT;
textBoxSPMC.Focus();
textBoxSPMC.SelectAll();
}
break;
case 117: //开始新订单 F6
if(isPressed)
{
resetSale(); //销售界面复位
}
break;
default:
//CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
break;
}
return(0);
}
///
/// 清理所有正在使用的资源。
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
C#中键盘钩子的使用
默认分类 2008-07-05 17:33 阅读53 评论0 字号: 大大 中中 小小 public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在这里放置你的处理代码 return 0;
}
}
使用方法
可以在Form的构造函数里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();
C#+低级Windows API钩子拦截键盘输入
www.diybl.com 时间:2010-01-14 作者:匿名 编辑:Smi1e 点击: 9 [评论]
-
-
摘要 在家里,婴儿和其它动物可能会重击你的计算机键盘,致使出现各种无法预言的结果。本文中的这个C#示例应用程序将向你展示如何基于Windows钩子API来实现在击键造成任何危害之前捕获它们。
一. 简介
猫和婴儿有很多共同之处。他们都喜欢吃家中养植的植物,都非常讨厌关门。他们也都爱玩弄你的键盘,结果是,你正发送给你的老板的电子邮件可能是以半截句子发送出去的,你的Excel帐户也被加入了一些乱七八糟的内容,并且你还没有注意到,当打开Windows资源管理器时,若干文件已经被移到了回收站!
其解决方案是,开发一个应用程序实现如下功能:只要键盘处于"威胁状态"你就可以进行切换,并确保任何键盘输入活动都不会造成危害。本文想展示如何使用一种低级Windows API钩子在一个C#应用程序中实现键盘"控制"。下图是本文示例程序的一个运行快照。
二. 背景
其实,已经存在许多有关于Windows钩子的文章和示例代码,并且已经有人编写过与本文几乎一样的C++示例程序。然而,当我搜索相应的C#应用程序的源码时,却找到极少的.NET示例,而且没有一个程序能够提供一个方便的自包含的C#类。
.NET框架能够使你以托管方式来存取你最常使用的键盘事件(通过KeyPress,KeyUp和KeyDown)。遗憾的是,这些事件都不能被用来停止Windows组合键(如Alt+Tab或Windows"开始"键),从而允许用户"远离"某一个应用程序。
本文的想法在操作系统级上捕获键盘事件而不是通过框架级来实现。为此,应用程序需要使用Windows API函数来把它自身添加到应用程序"钩子链"中以监听来自操作系统的键盘消息。当它收到这种类型的消息时,该应用程序能够选择性地传递消息,或者进行正常处理,或者"镇压"它以便不再有其它应用程序(包括Windows)来影响它。本文正是想解释其实现机理。
然而,请注意,本文中的代码仅适用于基于NT版本的Windows(NT,2000和XP),并且无法使用这个方法来停用Ctrl+Alt+Delete。有关于如何实现这一点,你可以参考MSDN有关资料。
三. 使用代码
为了易于使用,我在本文中提供了两个独立的zip文件。一个仅包含KeyboardHook类,这是本文介绍的重点。另一个是一个完整的微软Visual C# 2005 Express Edition应用程序工程,名叫"Baby Keyboard Bash",它实现显示击键的名字或彩色的形状以响应于击键。
四. 实例化类
键盘钩子是通过keyboard.cs中的KeyboardHook类来建立和管理的。这个类实现了IDisposable接口,因此,实例化它的最简单的方法是在应用程序的Main()方法中使用using关键字来封装Application.Run()调用。这将确保只要该应用程序开始即建立钩子并且,更重要的是,当该应用程序结束时立即使这个钩子失效。
这个类引发一个事件来警告应用程序已经有键被按下,因此主表单能够存取在Main()方法中创建的KeyboardHook实例就显得非常重要;最简单的方法是把这个实例存储在一个公共成员变量中。
KeyboardHook提供了三种构造器来启用或禁用某些设置:
· KeyboardHook():捕获所有击键,没有任何内容传递到Windows或另外的应用程序。
· KeyboardHook(string param):把参数串转换为Parameters枚举中的值之一,然后调用下面的构造器:
· KeyboardHook(KeyboardHook.Parameters enum):根据从Parameters枚举中选择的值的不同,分别启动下列设置:
o Parameters.AllowAltTab:允许用户使用Alt+Tab切换到另外的应用程序。
o Parameters.AllowWindowsKey:允许用户使用Ctrl+Esc或一种Windows键存取任务栏和开始菜单。
o Parameters.AllowAltTabAndWindows:启用Alt+Tab,Ctrl+Esc和Windows键。
o Parameters.PassAllKeysToNextApp:如果该参数为true,那么所有的击键将被传递给任何其它监听应用程序(包括Windows)。
当击键继续被键盘钩子捕获时,启用Alt+Tab和/或Windows键允许实际使用该计算机者切换到另一个应用程序并且使用鼠标与之交互。PassAllKeysToNextApp设置有效地禁用了击键捕获;这个类也是建立一个低级键盘钩子并且引发它的KeyIntercepted事件,但是它还负责把键盘事件传递到另一个监听程序。
因此,实例化该类以捕获所有击键的方法如下:
public static KeyboardHook kh;
[STAThread]
static void Main()
{
//其它代码
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}
五. 处理KeyIntercepted事件
当一外键被按下时,这个KeyboardHook类激活一个包含一些KeyboardHookEventArgs的KeyIntercepted事件。这是通过一个KeyboardHookEventHandler类型的方法使用以下方式来实现的:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
这个KeyboardHookEventArgs返回关于被按下键的下列信息:
· KeyName:键名,通过把捕获的键代码强制转换为System.Windows.Forms.Keys而获得。
· KeyCode:由键盘钩子返回的原来的键代码
· PassThrough:指出是否这个KeyboardHook实例被配置以允许该击键传递到其它应用程序。如果你想允许一用户使用Alt+Tab或 Ctrl+Esc/Windows键切换到其它的应用程序的话,那么对之进行检查是很有用的。
然后,使用一个具有适当签名的方法来执行击键所调用的任何任务。下面是一个示例片断:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
//检查是否这个键击事件被传递到其它应用程序并且停用TopMost,以防他们需要调到前端
if (e.PassThrough)
{
this.TopMost = false;
}
ds.Draw(e.KeyName);
}
本文的剩下部分将解释低级键盘钩子是如何在KeyboardHook中实现的。
六. 实现一个低级Windows API键盘钩子
在user32.dll中,Windows API包含三个方法来实现此目的:
· SetWindowsHookEx,它负责建立键盘钩子
· UnhookWindowsHookEx,它负责移去键盘钩子
· CallNextHookEx,它负责把击键信息传递到下一个监听键盘事件的应用程序
创建一个能够拦截键盘的应用程序的关键是,实现前面两个方法,而"放弃"第三个。结果是,任何击键都只能传递到这个应用程序中。
为了实现这一目标,第一步是包括System.Runtime.InteropServices命名空间并且导入API方法,首先是SetWindowsHookEx:
using System.Runtime.InteropServices
……
//在类内部:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
导入UnhookWindowsHookEx和CallNextHookEx的代码请见后面的讨论。
下一步是调用SetWindowsHookEx来建立钩子,这时需要传递下列四个参数:
· idHook:
这个数字决定了要建立的钩子的类型。例如,SetWindowsHookEx可以被用于钩住鼠标事件(当然还有其它事件)。在本文情况下,我们仅对13有兴趣,这是键盘钩子的id。为了使代码更易读些,我们把它赋值给一个常数WH_KEYBOARD_LL。
· Lpfn:
这是一个指向函数的长指针,该函数将负责处理键盘事件。在C#中,"指针"是通过传递一个代理类型的实例而获得的,从而使之引用一个适当的方法。这是我们在每次使用钩子时所调用的方法。
这里值得注意的是,这个代理实例需要被存储于这个类的一个成员变量中。这是为了防止一旦第一个方法调用结束它会被作为垃圾回收。
· hMod:
建立钩子的应用程序的一个实例句柄。我找到的绝大多数实例仅把它设置为IntPtr.Zero,理由是不大可能存在该应用程序的多个实例。然而,这部分代码使用了来自于kernel32.dll的GetModuleHandle来标识准确的实例从而使这个类更具灵活性。
· dwThreadId:
当前进程的id。把它设置为0可以使这个钩子成为全局构子,这是相应于一个低级键盘钩子的正确设置。
SetWindowsHookEx返回一个钩子id,这个id将被用于当应用程序结束时从钩子链中脱钩,因此它需要存储在一个成员变量中以备将来使用。KeyboardHook类中的相关代码如下:
private HookHandlerDelegate proc;
private IntPtr hookID = IntPtr.Zero;
private const int WH_KEYBOARD_LL = 13;
public KeyboardHook()
{
proc = new HookHandlerDelegate(HookCallback);
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
hookID = SetWindowsHookEx(WH_KEYBOARD_LL, proc,GetModuleHandle(curModule.ModuleName), 0);
}
}
七. 处理键盘事件
如前面所提及,SetWindowsHookEx需要一个到被用来处理键盘事件的回调函数的指针。它期望有一个使用如下签名的函数:
LRESULT CALLBACK LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam);
其实,建立一个函数指针的C#方法使用了一个代理,因此,向SetWindowsHookEx指出它需要的内容的第一步是使用正确的签名来声明一个代理:
private delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
然后,使用相同的签名编写一个回调方法;这个方法将包含实际上处理键盘事件的所有代码。在KeyboardHook的情况下,它检查是否击键应该被传递给其它应用程序并且接下来激发KeyIntercepted事件。下面是一个简化版本的不带有击键处理代码的情况:
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
//仅为KeyDown事件过滤wParam,否则该代码将再次执行-对于每一次击键(也就是,相应于KeyDown和KeyUp)
//WM_SYSKEYDOWN是捕获Alt相关组合键所必需的
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
//激发事件
OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
//返回一个"哑"值以捕获击键
return (System.IntPtr)1;
}
//事件没有被处理,把它传递给下一个应用程序
return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
接下来,一个到HookCallback的参考被指派给HookHandlerDelegate的一个实例并且被传递到SetWindowsHookEx的调用,正如前一节所展示的。
无论何时一个键盘事件发生,下列参数将被传递给HookCallBack:
· nCode:
根据MSDN文档,回调函数应该返回CallNextHookEx的结果,如果这个值小于零的话。正常的键盘事件将返回一个大于或等于零的nCode值。
· wParam:
这个值指示发生了什么类型的事件:键被按下还是松开,以及是否按下的键是一个系统键(左边或右边的Alt键)。
· lParam:
这是一个存储精确击键信息的结构,例如被按键的代码。在KeyboardHook中声明的这个结构如下:
private struct KBDLLHOOKSTRUCT
{
public int vkCode;
int scanCode;
public int flags;
int time;
int dwExtraInfo;
}
其中的这两个公共参数是在KeyboardHook中的回调方法所使用的仅有的两个参数。vkCoke返回虚拟键代码,它能够被强制转换为System.Windows.Forms.Keys以获得键名,而flags显示是否这是一个扩展键(例如,Windows Start键)或是否同时按下了Alt键。有关于Hook回调方法的完整代码展示在每一种情况下要检查哪些flags值。
如果flags提供的信息和KBDLLHOOKSTRUCT的其它组成元素不需要,那么这个回调方法和代码的签名可以按如下进行修改:
private delegate IntPtr HookHandlerDelegate(
int nCode, IntPtr wParam, IntPtr lParam);
在这种情况中,lParam将仅返回vkCode。
八. 把击键传递到下一个应用程序
一个良好的键盘钩子回调方法应该以调用CallNextHookEx函数并且返回它的结果结束。这可以确保其它应用程序能够有机会处理针对于它们的击键。
然而,KeyboardHook类的主要功能在于,阻止击键被传播到任何其它更多的应用程序。因此它无论在何时处理一次击键,HookCallback都将返回一个哑值:
return (System.IntPtr)1;
另一方面,它确实调用CallNextHookEx-如果它不处理该事件,或如果重载的构造器中的使用KeyboardHook传递的参数允许某些组合键通过。
CallNextHookEx被启用-通过从user32.dll导入该函数,如下列代码所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KeyInfoStruct lParam);
然后,被导入的方法被HookCallMethod所调用,这可以确保所有的通过钩子接收到的参数被继续传递到下一个应用程序中:
CallNextHookEx(hookID, nCode, wParam, ref lParam);
如前面所提及,如果在lParam中的flags是不相关的,那么可以修改导入的CallNextHookEx的签名以把lParam定义为System.IntPtr。
九. 移去钩子
处理钩子的最后一步是使用从user32.dll中导入的UnhookWindowsHookEx函数移去它(当破坏KeyboardHook类的实例时),如下所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
既然KeyboardHook实现IDisposable,那么这可以在Dispose方法中完成。
public void Dispose()
{
UnhookWindowsHookEx(hookID);
}
hookID是构造器在调用SetWindowsHookEx所返回的id。这将从钩子链中删除应用程序。
c#键盘钩子
作者: lzh 类别: C#/VB 日期: 2003-5-27 22:22:19
You can use system hook to hook any messages send to the application.
Hooking the keyboard (WH_KEYBOARD) and mouse (WH_MOUSE) messages can be
used to determine if the user interactive with the computer. You also can
hook all the messages (WH_GETMESSAGE). The hook can be set with this code:
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_GETMESSAGE = 3
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_GETMESSAGE,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//Perform your process
return 0;
}
}
Then you can install the hook procedure by the following code:
Win32Hook hook = new Win32Hook();
hook.SetHook();
用钩子(hook)实现C#的屏幕键盘效果
-
SVN技术网 www.svn8.com 2010-01-20 08:38:02 来源:bbs.svn8.com 作者:佚名 点击:158次
文章摘要:用钩子(hook)实现C#的屏幕键盘效果 要实现一个屏幕键盘,需要监听所有键盘事件,无论窗体是否被激活。因此需要一个全局的钩子,也就 是系统范围的钩子。 什么是钩子(Hook) 钩子(Hook)是Windows提供的一种消息处理机制平台,是指在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)
-
使用完钩子后,要进行卸载,这个可以写在析构函数中。
12 public void Stop() {3 this.Stop(true, true, true);4 }5 6 public void Stop(bool uninstallMouseHook, bool uninstallKeyboardHook, 7 bool throwExceptions) {8 // if mouse hook set and must be uninstalled9 if (hMouseHook != IntPtr.Zero && uninstallMouseHook) {10 // uninstall hook11 bool retMouse = UnhookWindowsHookEx(hMouseHook);12 // reset invalid handle13 hMouseHook = IntPtr.Zero;14 // if failed and exception must be thrown15 if (retMouse == false && throwExceptions) {16 // Returns the error code returned by the last unmanaged function 17 // called using platform invoke that has the DllImportAttribute.18 // SetLastError flag set. 19 int errorCode = Marshal.GetLastWin32Error();20 // Initializes and throws a new instance of the Win32Exception class 21 // with the specified error. 22 throw new Win32Exception(errorCode);23 }24 }2526 // if keyboard hook set and must be uninstalled27 if (hKeyboardHook != IntPtr.Zero && uninstallKeyboardHook) {28 // uninstall hook29 bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook);30 // reset invalid handle31 hKeyboardHook = IntPtr.Zero;32 // if failed and exception must be thrown33 if (retKeyboard == false && throwExceptions) {34 // Returns the error code returned by the last unmanaged function 35 // called using platform invoke that has the DllImportAttribute.36 // SetLastError flag set. 37 int errorCode = Marshal.GetLastWin32Error();38 // Initializes and throws a new instance of the Win32Exception class 39 // with the specified error. 40 throw new Win32Exception(errorCode);41 }42 }43 }44
将这个文件编译成一个dll,即可在应用程序中调用。通过它提供的事件,便可监听所有的键盘事件。
但是,这只能监听键盘事件,没有键盘的情况下,怎么会有键盘事件?其实很简单,通过SendInput
API函数提供虚拟键盘代码的调用即可模拟键盘输入。下面的代码模拟一个 KeyDown 和 KeyUp 过程,
把他们连接起来就是一次按键过程。
1 private void SendKeyDown(short key) {2 Input[] input = new Input[1];3 input[0].type = INPUT.KEYBOARD;4 input[0].ki.wVk = key;5 input[0].ki.time = NativeMethods.GetTickCount();67 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0])) 8 < input.Length) {9 throw new Win32Exception(Marshal.GetLastWin32Error());10 }11 }1213 private void SendKeyUp(short key) {14 Input[] input = new Input[1];15 input[0].type = INPUT.KEYBOARD;16 input[0].ki.wVk = key;17 input[0].ki.dwFlags = KeyboardConstaint.KEYEVENTF_KEYUP;18 input[0].ki.time = NativeMethods.GetTickCount();1920 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0]))21 < input.Length) {22 throw new Win32Exception(Marshal.GetLastWin32Error());23 }24 }
自己实现一个 KeyBoardButton 控件用作按钮,用 Visual Studio 或者 SharpDevelop 为屏幕键盘设计 UI,然后
在这些 Button 的 Click 事件里面模拟一个按键过程。
12 private void ButtonOnClick(object sender, EventArgs e) {3 KeyboardButton btnKey = sender as KeyboardButton;4 if (btnKey == null) {5 return;6 }78 SendKeyCommand(btnKey);9 }10 11 private void SendKeyCommand(KeyboardButton keyButton) {12 short key = keyButton.VKCode;13 if (combinationVKButtonsMap.ContainsKey(key)) {14 if (keyButton.Checked) {15 SendKeyUp(key);16 } else {17 SendKeyDown(key);18 }19 } else {20 SendKeyDown(key);21 SendKeyUp(key);22 }23 }
其中 combinationVKButtonsMap 是一个 IDictionary>, key 存储的是VK_SHIFT, VK_CONTROL 等组合键的键盘码。左右两个按钮对应同一个键盘码,因此需要放在一个 List 里。
标准键盘上的每一个键都有虚拟键码( VK_CODE)与之对应。还有一些其他的常量,
把它写在一个静态 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_COMMA = 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 }
文章来自[SVN中文技术网]转发请保留本站地址:http://www.svn8.com/dotnet/Csharp/2010012018328_2.html
C#中键盘钩子的使用(转)
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在这里放置你的处理代码 return 0;
}
}
使用方法
可以在Form的构造函数里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();