c#调用钩子
1 概述 
在c++中有钩子程序,但是在C#还没有对其进行封装,所以需要自己根据实际情况调用钩子。钩子在我的理解下是,通过初始化钩子与系统中消息映射建立某种关系,当点击鼠标或者键盘,就会通过钩子中的回调函数获取信息。 
钩子分为全局钩子和私有钩子 
2 编写流程 
a 从c++中导入,需要自己添加导入函数。 
代码为:
 [DllImport("user32.dll")]
 static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);
  [DllImport("user32.dll")]
   static extern bool UnhookWindowsHookEx(IntPtr hhk);
   [DllImport("user32.dll")]
   static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
   [DllImport("user32.dll")]
     static extern short GetKeyState(VirtualKeys nVirtKey);
b 定义委托和事件 
为了方便在注册后,hook能获取消息,可以通过委托和事件。
//SetWindowsHookEx调用。
public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
//用于获取消息
private static HookProc MouseHookProcedure;
private static HookProc KeyboardHookProcedure;
事件 #region Events /// <summary>
/// 当用户移动鼠标,按下任意鼠标按钮或滚动的车轮
/// </summary>
public event MouseEventHandler OnMouseActivity;
/// <summary>
/// 发生时,用户按下一个键
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// 当用户按下并释放
/// </summary>
public event KeyPressEventHandler KeyPress;
/// <summary>
/// 当用户释放的关键
/// </summary>
public event KeyEventHandler KeyUp; #endregion
c 定义Hooktype
public enum HookType : int
{
WH_JOURNALRECORD = ,
WH_JOURNALPLAYBACK = ,
WH_KEYBOARD = ,//私有钩子定义
WH_GETMESSAGE = ,
WH_CALLWNDPROC = ,
WH_CBT = ,
WH_SYSMSGFILTER = ,
WH_MOUSE = ,
WH_HARDWARE = ,
WH_DEBUG = ,
WH_SHELL = ,
WH_FOREGROUNDIDLE = ,
WH_CALLWNDPROCRET = ,
WH_KEYBOARD_LL = ,//全局钩子定义
WH_MOUSE_LL =
}
d hook类的定义和实现
public HookApi(bool InstallMouseHook, bool InstallKeyboardHook)
{
Start(InstallMouseHook, InstallKeyboardHook);
} #region Hook Handling public void Start(bool InstallMouseHook, bool InstallKeyboardHook)
{
//定义鼠标钩子函数
if (hMouseHook == && InstallMouseHook)
{
//关联鼠标事件
MouseHookProcedure = new HookProc(MouseHookProc);
//IntPtr ha=Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
IntPtr ha = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
//注册为全局钩子,后面会讲解
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, ha, );
//hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure,IntPtr.Zero, 0); if (hMouseHook == )
{
int errorCode = Marshal.GetLastWin32Error();
Stop(true, false, false);
throw new Win32Exception(errorCode);
//throw new Exception(errorCode.ToString());
}
}
if (hKeyboardHook == && InstallKeyboardHook)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
//IntPtr ha = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
IntPtr ha = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, ha, );
//hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProcedure,IntPtr.Zero, 0);
if (hKeyboardHook == )
{
int errorCode = Marshal.GetLastWin32Error();
Stop(false, true, false);
throw new Win32Exception(errorCode);
//throw new Exception(errorCode.ToString());
}
}
} private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
if ((nCode >= ) && (OnMouseActivity != null))
{
MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct)); MouseButtons button = MouseButtons.None;
short mouseDelta = ;
switch (wParam)
{
case WM_LBUTTONDOWN:
//case WM_LBUTTONUP:
//case WM_LBUTTONDBLCLK:
button = MouseButtons.Left;
break;
case WM_RBUTTONDOWN:
//case WM_RBUTTONUP:
//case WM_RBUTTONDBLCLK:
button = MouseButtons.Right;
break;
case WM_MOUSEWHEEL:
//如果消息是WM_MOUSEWHEEL,高阶字的mouseData成员车轮三角洲。
//点击一个车轮被定义为,这是120 WHEEL_DELTA
//(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value
mouseDelta = (short)((mouseHookStruct.mouseData >> ) & 0xffff);
//TODO: X BUTTONS (I havent them so was unable to test)
//如果消息是WM_XBUTTONUP WM_XBUTTONDOWN,WM_XBUTTONDBLCLK,WM_NCXBUTTONDOWN,WM_NCXBUTTONUP
//或WM_NCXBUTTONDBLCLK的,高阶字指定X键被按下或释放,
//低位字被保留。这个值可以是以下值中的一个或多个。
//否则,mouseData还没有使用。
break;
}
int clickCount = ;
if (button != MouseButtons.None)
if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK)
clickCount = ;
else clickCount = ; MouseEventArgs e = new MouseEventArgs(button, clickCount, mouseHookStruct.pt.x, mouseHookStruct.pt.y, mouseDelta);
OnMouseActivity(this, e);
}
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
//键盘事件响应函数,在注册后,就可以在这函数添加响应
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
try
{
bool handled = false;
if ((nCode >= ) && (KeyDown != null || KeyUp != null || KeyPress != null))
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)
Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
handled = handled || e.Handled;
}
if (KeyPress != null && wParam == WM_KEYDOWN)
{
bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (GetKeyState(VK_CAPITAL) != ? true : false); byte[] keyState = new byte[];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[];
if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == )
{
char key = (char)inBuffer[];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key))
{
key = Char.ToUpper(key);
}
KeyPressEventArgs e = new KeyPressEventArgs(key);
KeyPress(this, e);
handled = handled || e.Handled;
}
}
if (KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUp(this, e);
handled = handled || e.Handled;
} }
if (handled)
return ;
else
return ;//CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
catch (Exception ex)
{
return -;
}
} public void Stop()
{
this.Stop(true, true, true);
}
//钩子卸载
public void Stop(bool UninstallMouseHook, bool UninstallKeyboardHook, bool ThrowExceptions)
{
if (hMouseHook != && UninstallMouseHook)
{
int retMouse = UnhookWindowsHookEx(hMouseHook);
hMouseHook = ;
if (retMouse == && ThrowExceptions)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
}
if (hKeyboardHook != && UninstallKeyboardHook)
{
int retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = ;
if (retKeyboard == && ThrowExceptions)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
}
}
e 钩子函数调用
HookApi hook ;
if (HcHook == null)
{
HcHook = new HookApi(false, true);
}
HcHook.KeyDown += new KeyEventHandler(HcHook_KeyDown);
HcHook.KeyUp += new KeyEventHandler(HcHook_KeyUp);
void HcHook_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
Cat.Log.FileLog.WriteMessage("CCVSifhtPanel中按下Ctrl");
FlagKeyborad = true;
} }
3 注意事项 
下面备注一下在使用过程中一些类库说明及注意事项:
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);
代码
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/ -->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)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。
c#调用钩子的更多相关文章
- windows消息钩子注册底层机制浅析
		
标 题: [原创]消息钩子注册浅析 作 者: RootSuLe 时 间: 2011-06-18,23:10:34 链 接: http://bbs.pediy.com/showthread.php?t= ...
 - php 添加钩子实例
		
<?php/*定义钩子函数*/function add($hook,$actionFunc){ global $emHooks; if(isset($emHooks[$hook])) ...
 - WordPress 插件机制的简单用法和原理(Hook 钩子)
		
WordPress 的插件机制实际上只的就是这个 Hook 了,它中文被翻译成钩子,允许你参与 WordPress 核心的运行,是一个非常棒的东西,下面我们来详细了解一下它. PS:本文只是简单的总结 ...
 - C# Hook钩子实例代码之截取键盘输入,需要的朋友可以参考下
		
一.关于本文 以最通俗的语言说明钩子的使用方法,具体到钩子的详细介绍可以参照下面的网址: http://www.microsoft.com/china/community/program/origin ...
 - C#钩子应用实例
		
C#钩子应用实例一.写在最前 本文的内容只想以最通俗的语言说明钩子的使用方法,具体到钩子的详细介绍可以参照下面的网址: http://www.microsoft.com/china/community ...
 - HOOK钩子 - 钩子函数说明
		
翻译参考自MaybeHelios的blog: http://blog.csdn.net/maybehelios/ 通过SetWindowsHookEx方法安装钩子,该函数指定处理拦截消息的钩子函数(回 ...
 - HOOK API (一)——HOOK基础+一个鼠标钩子实例
		
HOOK API (一)——HOOK基础+一个鼠标钩子实例 0x00 起因 最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护.原本想从内核层实现,但没有头绪.最后决定从调用层入手 ...
 - 《windows核心编程系列》十八谈谈windows钩子
		
windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...
 - VC6  鼠标钩子 最简单样例
		
Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的.而钩子是Windows系统中非常重要的系统接口,用它能够截获并处理送给其它应用程序的消息,来完毕普通应用程序 ...
 
随机推荐
- boost::bind实践
			
第一部分源码为基础实践: /*Beyond the C++ Standard Library ( An Introduction to Boost )[CN].chm*/ /*bind的用法*/ #i ...
 - hibernate映射
			
三种方式: 持久化注解 目前开发主流方式 XML配置描述文件(XML deployment descriptor,可以让Hibernate的PO类与JPA实体类兼容,实际中很少用) ...
 - Android Capability 细粒度的权限控制
			
1. 传统的UID/GID,权限颗粒度太大 2. Capability: 细粒度的权限控制 3. 进程的Capability 4. 文件的Capability 5. 进程的Capability Bou ...
 - memcached在linux安装
			
服务器端主要是安装memcache服务器端.下载:http://www.danga.com/memcached/dist/memcached-1.2.2.tar.gz另外,Memcache用到了lib ...
 - paramiko学习
			
1. ssh简介 2. ssh私有key/共有key的区别 3. paramiko的常规使用
 - QSslError 类
			
QSslError Class Header: #include <QSslError> qmake: QT += network Since: Qt 4.3 注意:这个类中的所有函数都是 ...
 - Shell之test
			
test命令用法.功能:检查文件和比较值 1)判断表达式 if test (表达式为真) if test !表达式为假 test 表达式1 –a 表达式2 两个表达式 ...
 - JAVA NIO 结合多线程
			
NIO 的选择器采用了多路复用(Multiplexing)技术,可在一个选择器上处理多个套接字, 通过获取读写通道来进行 IO 操作.由于网络带宽等原因,在通道的读.写操作中是容易出现等待的, 所以在 ...
 - SendMail
			
public ActionResult SendMail() { MailMessage mss = new MailMessage(); mss.From = new MailAddress(&qu ...
 - 领域驱动设计(Domain Driven Design)参考架构详解
			
摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...