C#键盘钩子

syx920301 2011-02-07 09:45:41
我用键盘钩子 模拟passwordchar 但是有一个问题就是 为什么敲击键盘后字符要在文本框出现瞬间才能变为* 但是passwordchar属性是直接就变*了 字符没有在敲击键盘后还有停留一瞬间情况出现 为什么
...全文
1106 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
syx920301 2011-02-07
  • 打赏
  • 举报
回复
我主要是想练习钩子
threenewbee 2011-02-07
  • 打赏
  • 举报
回复
用不着键盘钩子。

SubClass 文本框就可以了。
wukaixin 2011-02-07
  • 打赏
  • 举报
回复
键盘钩子包括两类:全局钩子和私有钩子,这里我分成两个类来设计:





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 Chen




www.phpzy.com www.shopfw.com www.jiemengwu.com
doubleu2005 2011-02-07
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 syx920301 的回复:]

能不能共享一下
[/Quote]
就是参考这个
http://www.gettop.net/DownLoad/ShowInfo.asp?ID=12
syx920301 2011-02-07
  • 打赏
  • 举报
回复
能不能共享一下
doubleu2005 2011-02-07
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 syx920301 的回复:]

C# code
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
{

if (lParam.ToInt32() > 0)
……
[/Quote]
我的是先注册一个keydown事件,在这里面进行处理
xu56180825 2011-02-07
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 cdcjk 的回复:]
请问用钩子来做什么
[/Quote]
我觉得应该是用来钓鱼的吧
syx920301 2011-02-07
  • 打赏
  • 举报
回复
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);
}
doubleu2005 2011-02-07
  • 打赏
  • 举报
回复
把你处理字符的代码贴上来,看看你是在那块处理的
syx920301 2011-02-07
  • 打赏
  • 举报
回复
能举个简单的例子吗 我刚接触这些东西
doubleu2005 2011-02-07
  • 打赏
  • 举报
回复
LZ的钩子用的有问题,应该在你按键后显示前截获字符让其显示为*,而你这个是显示后再处理为*,你应该在keydown时就进行截获处理
cdcjk 2011-02-07
  • 打赏
  • 举报
回复
请问用钩子来做什么

111,119

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧