C#键盘钩子 我用键盘钩子 模拟passwordchar 但是有一个问题就是 为什么敲击键盘后字符要在文本框出现瞬间才能变为* 但是passwordchar属性是直接就变*了 字符没有在敲击键盘后还有停留一瞬间情况出现 为什么 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 键盘钩子包括两类:全局钩子和私有钩子,这里我分成两个类来设计: public delegate int HOOKPROC(int nCode, int wParam, int lParam); public enum HookType { WH_KEYBOARD = 2,//私有钩子 WH_KEYBOARD_LL = 13//全局钩子 } Hook基类实现 public abstract class hook { //设置钩子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern int SetWindowsHookEx(HookType idHook, HOOKPROC lpfn, IntPtr hInstance, int threadId); //抽调钩子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern bool UnhookWindowsHookEx(int idHook); [DllImport("kernel32")] public static extern int GetCurrentThreadId(); /// <summary> /// 钩子处理委托 /// </summary> public HOOKPROC proc; /// <summary> /// 钩子类型 /// </summary> public HookType type; /// <summary> /// 钩子的句柄 /// </summary> public int hHook = 0; public hook(HOOKPROC proc, HookType type) { this.proc = proc; this.type = type; } public abstract int SetWindowsHookEx(); public virtual void UnhookWindowsHookEx() { bool retKeyboard = true; if (hHook != 0) { retKeyboard = UnhookWindowsHookEx(hHook); hHook = 0; } if (!retKeyboard) { throw new Exception("UnhookWindowsHookEx failed."); } } } 全局钩子和私有钩子实现 public class PublicHook : hook { public PublicHook(HOOKPROC proc) : base(proc, HookType.WH_KEYBOARD_LL) { } public override int SetWindowsHookEx() { if (hHook == 0) hHook = SetWindowsHookEx(this.type, this.proc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); return hHook; } } public class PrivateHook : hook { public PrivateHook(HOOKPROC proc) : base(proc, HookType.WH_KEYBOARD) { } public override int SetWindowsHookEx() { if (hHook == 0) hHook = SetWindowsHookEx(this.type, this.proc, IntPtr.Zero, GetCurrentThreadId()); return hHook; } } 主界面调用实现 private hook hook = null;private void btnOpen_Click(object sender, EventArgs e){ if (this.comboBox1.SelectedIndex == 0) hook = new PublicHook(MyKeyboardProc); else hook = new PrivateHook(MyKeyboardProc); int hHook = hook.SetWindowsHookEx(); if (hHook == 0) { MessageBox.Show("设置钩子失败!"); }}private void btnClose_Click(object sender, EventArgs e){ try { if (hook != null) hook.UnhookWindowsHookEx(); } catch { MessageBox.Show("关闭钩子失败!"); }}public int MyKeyboardProc(int nCode, int wParm, int lParam){ MessageBox.Show("你已经按下了按钮!"); return 0;} 运行后主界面效果如下图所示: 下面备注一下在使用过程中一些类库说明及注意事项:1、AppDomain.GetCurrentThreadId()在.net 2.0中过时了,VS2005和VS2008警告这个方法已经过时,建议使用System.Threading.Thread.CurrentThread.ManagedThreadId,但实际上这两个值是不一样的。AppDomain.GetCurrentThreadId()的实际上调用Win32 API,其返回的是该线程在Windows中的ThreadId,即同这个等价:[DllImport("kernel32")]public static extern int GetCurrentThreadId();而System.Threading.Thread.CurrentThread.ManagedThreadId返回的是作为一个ManagedThread在.Net CLR中的ThreadId,所以这和Windows的ThreadId是完全不同的。2、使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数.函数签名如下:[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam); 代码 HHOOK SetWindowsHookEx( int idHook, // 钩子的类型,即它处理的消息类型 HOOKPROC lpfn, // 钩子子程的地址指针。如果dwThreadId参数为0 // 或是一个由别的进程创建的线程的标识, // lpfn必须指向DLL中的钩子子程。 // 除此以外,lpfn可以指向当前进程的一段钩子子程代码。 // 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。 HINSTANCE hMod, // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。 // 如果dwThreadId 标识当前进程创建的一个线程, // 而且子程代码位于当前进程,hMod必须为NULL。 // 可以很简单的设定其为本应用程序的实例句柄。 DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。 // 如果为0,钩子子程与所有的线程关联,即为全局钩子。 ); 函数成功则返回钩子子程的句柄,失败返回NULL。以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。 3、系统钩子和线程钩子:SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。 线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。 系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。几点说明:(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。4、钩子处理函数:public int MyKeyboardProc(int nCode, int wParm, int lParam)注意:如果返回1,则结束消息,这个消息到此为止,不再传递。如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者.可以添加自己想要的信息处理(如发邮件,增加文件等)这里可以添加自己想要的信息处理 public int MyKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct)); if (kbh.vkCode == (int)Keys.S && (int)Control.ModifierKeys == (int)Keys.Control) // 截获F8 { MessageBox.Show("快捷键已拦截!不能保存!"); return 1; } if ((int)Control.ModifierKeys == (int)Keys.Delete && (int)Control.ModifierKeys == (int)Keys.Alt && (int)Control.ModifierKeys == (int)Keys.Control) { MessageBox.Show("捕捉到Ctrl+Alt+Delete"); return 1; } if (kbh.vkCode == (int)Keys.Y && (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt) //截获Ctrl+Alt+Y { About msg = new About(); msg.Show(); MessageBox.Show("不能全部保存!"); return 1; } if (kbh.vkCode == (int)Keys.A) { MessageBox.Show("A"); this.label1.Text += "A"; } if (kbh.vkCode == (int)Keys.B) { MessageBox.Show("B"); this.label1.Text += "B"; } if (kbh.vkCode == (int)Keys.Enter) { this.label1.Text = "执行成功!"; } if (kbh.vkCode == (int)Keys.Back) { this.label1.Text = this.label1.Text.Remove(this.label1.Text.Length - 1); } if (kbh.vkCode == (int)Keys.D1) { this.label1.Text += "1"; } } return CallNextHookEx(hHook, nCode, wParam, lParam); } //键盘Hook结构函数(详情查看msdn的Platform SDK中的KBDLLHOOKSTRUCT结构) [StructLayout(LayoutKind.Sequential)] public class KeyBoardHookStruct { public int vkCode;//表达一个在1到254间的虚拟键盘码 public int scanCode;//表示硬件扫描码 public int flags; public int time; public int dwExtraInfo; }5、钩子函数执行两次的解决办法:http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html最后,欢迎朋友们进行指正,谢谢! Best Reagards,Charles Chenwww.phpzy.com www.shopfw.com www.jiemengwu.com 用不着键盘钩子。SubClass 文本框就可以了。 LZ的钩子用的有问题,应该在你按键后显示前截获字符让其显示为*,而你这个是显示后再处理为*,你应该在keydown时就进行截获处理 private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) { if (nCode >= 0) { if (lParam.ToInt32() > 0) { textBox1.Text = "*"; } return 0; } return CallNextHoolEx(hKeyboardHook, nCode, wParam, lParam); } 我的是先注册一个keydown事件,在这里面进行处理 就是参考这个http://www.gettop.net/DownLoad/ShowInfo.asp?ID=12 未将对象引用设置到对象的实例 求助:如何将2011年4月转换成2011-04 C# 模仿金山词霸界面 改变datagrid的输出显示`` 简单提问=直接送分 在线急等!!!列表设置问题。急急急!!! C#form中dataGridView1控件中双击任一行的任意单元格时,返回出那一行的个人信息号怎么实现? 求一正则 我发现.NET不能跨windows平台开发 MSN.COM 的EMAIL,能在OUTLOOK2000里接收和发送吗?POP3和SMTP是多少? C# MVC 使用JQuery DatePicker 出问题,捣鼓好几天了 WCF中使用Stream来传送文件,应该如何通知进度?
public enum HookType
{
WH_KEYBOARD = 2,//私有钩子
WH_KEYBOARD_LL = 13//全局钩子
}
Hook基类实现
public abstract class hook
{
//设置钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(HookType idHook, HOOKPROC lpfn, IntPtr hInstance, int threadId);
//抽调钩子
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook); [DllImport("kernel32")]
public static extern int GetCurrentThreadId(); /// <summary>
/// 钩子处理委托
/// </summary>
public HOOKPROC proc;
/// <summary>
/// 钩子类型
/// </summary>
public HookType type;
/// <summary>
/// 钩子的句柄
/// </summary>
public int hHook = 0; public hook(HOOKPROC proc, HookType type)
{
this.proc = proc;
this.type = type;
} public abstract int SetWindowsHookEx();
public virtual void UnhookWindowsHookEx()
{
bool retKeyboard = true;
if (hHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hHook);
hHook = 0;
}
if (!retKeyboard)
{
throw new Exception("UnhookWindowsHookEx failed.");
}
}
}
全局钩子和私有钩子实现
public class PublicHook : hook
{
public PublicHook(HOOKPROC proc)
: base(proc, HookType.WH_KEYBOARD_LL)
{ } public override int SetWindowsHookEx()
{
if (hHook == 0)
hHook = SetWindowsHookEx(this.type, this.proc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
return hHook;
}
} public class PrivateHook : hook
{
public PrivateHook(HOOKPROC proc)
: base(proc, HookType.WH_KEYBOARD)
{ } public override int SetWindowsHookEx()
{
if (hHook == 0)
hHook = SetWindowsHookEx(this.type, this.proc, IntPtr.Zero, GetCurrentThreadId());
return hHook;
}
}
主界面调用实现
private hook hook = null;
private void btnOpen_Click(object sender, EventArgs e)
{
if (this.comboBox1.SelectedIndex == 0)
hook = new PublicHook(MyKeyboardProc);
else
hook = new PrivateHook(MyKeyboardProc);
int hHook = hook.SetWindowsHookEx();
if (hHook == 0)
{
MessageBox.Show("设置钩子失败!");
}
}
private void btnClose_Click(object sender, EventArgs e)
{
try
{
if (hook != null)
hook.UnhookWindowsHookEx();
}
catch
{
MessageBox.Show("关闭钩子失败!");
}
}public int MyKeyboardProc(int nCode, int wParm, int lParam)
{
MessageBox.Show("你已经按下了按钮!");
return 0;
} 运行后主界面效果如下图所示: 下面备注一下在使用过程中一些类库说明及注意事项:1、AppDomain.GetCurrentThreadId()在.net 2.0中过时了,VS2005和VS2008警告这个方法已经过时,建议使用System.Threading.Thread.CurrentThread.ManagedThreadId,但实际上这两个值是不一样的。AppDomain.GetCurrentThreadId()的实际上调用Win32 API,其返回的是该线程在Windows中的ThreadId,即同这个等价:[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
而System.Threading.Thread.CurrentThread.ManagedThreadId返回的是作为一个ManagedThread在.Net CLR中的ThreadId,所以这和Windows的ThreadId是完全不同的。2、使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数.函数签名如下:[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
代码
HHOOK SetWindowsHookEx(
int idHook, // 钩子的类型,即它处理的消息类型
HOOKPROC lpfn, // 钩子子程的地址指针。如果dwThreadId参数为0
// 或是一个由别的进程创建的线程的标识,
// lpfn必须指向DLL中的钩子子程。
// 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
// 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
HINSTANCE hMod, // 应用程序实例的句柄。标识包含lpfn所指的子程的
DLL。
// 如果dwThreadId 标识当前进程创建的一个线程,
// 而且子程代码位于当前进程,hMod必须为NULL。
// 可以很简单的设定其为本应用程序的实例句柄。
DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
// 如果为0,钩子子程与所有的线程关联,即为全局钩子。
);
函数成功则返回钩子子程的句柄,失败返回NULL。以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。 3、系统钩子和线程钩子:SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。
线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。
系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。
几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。
(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。
(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。
4、钩子处理函数:public int MyKeyboardProc(int nCode, int wParm, int lParam)注意:如果返回1,则结束消息,这个消息到此为止,不再传递。如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者.可以添加自己想要的信息处理(如发邮件,增加文件等)这里可以添加自己想要的信息处理
public int MyKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
if (kbh.vkCode == (int)Keys.S && (int)Control.ModifierKeys == (int)Keys.Control) // 截获F8
{
MessageBox.Show("快捷键已拦截!不能保存!");
return 1;
}
if ((int)Control.ModifierKeys == (int)Keys.Delete && (int)Control.ModifierKeys == (int)Keys.Alt && (int)Control.ModifierKeys == (int)Keys.Control)
{
MessageBox.Show("捕捉到Ctrl+Alt+Delete");
return 1;
}
if (kbh.vkCode == (int)Keys.Y
&& (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt) //截获Ctrl+Alt+Y
{
About msg = new About();
msg.Show();
MessageBox.Show("不能全部保存!");
return 1;
}
if (kbh.vkCode == (int)Keys.A)
{
MessageBox.Show("A");
this.label1.Text += "A";
}
if (kbh.vkCode == (int)Keys.B)
{
MessageBox.Show("B");
this.label1.Text += "B";
}
if (kbh.vkCode == (int)Keys.Enter)
{
this.label1.Text = "执行成功!";
}
if (kbh.vkCode == (int)Keys.Back)
{
this.label1.Text = this.label1.Text.Remove(this.label1.Text.Length - 1);
}
if (kbh.vkCode == (int)Keys.D1)
{
this.label1.Text += "1";
}
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
//键盘Hook结构函数(详情查看msdn的Platform SDK中的KBDLLHOOKSTRUCT结构)
[StructLayout(LayoutKind.Sequential)]
public class KeyBoardHookStruct
{
public int vkCode;//表达一个在1到254间的虚拟键盘码
public int scanCode;//表示硬件扫描码
public int flags;
public int time;
public int dwExtraInfo;
}
5、钩子函数执行两次的解决办法:http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html最后,欢迎朋友们进行指正,谢谢!
Best Reagards,Charles Chen
www.phpzy.com www.shopfw.com www.jiemengwu.com
{
if (nCode >= 0)
{
if (lParam.ToInt32() > 0)
{
textBox1.Text = "*";
}
return 0;
}
return CallNextHoolEx(hKeyboardHook, nCode, wParam, lParam);
}
http://www.gettop.net/DownLoad/ShowInfo.asp?ID=12