这几天用C#做了一个抓图软件,其中用到了系统钩子技术。
在这里发表下自己用C#实现系统钩子的心得,希望对我的朋友们有所帮助。
看了很多资料,都提到系统钩子(Hook)必须是一个dll,这样Windows才能在所有进程中共享钩子代码。
与钩子相关的3个Windows API函数是:
SetWindowsHookEx、UnhookWindowsHookEx、CallNextHookEx,它们的简要说明如下:
HHOOK SetWindowsHookEx(
int
idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
idHook -- 系统钩子类型,实际上是一个int值,具体的定义请看MSDN
lpfn -- 钩子回调函数指针,当收到钩子消息时就执行这个回调函数,回调函数有3个参数,不同类型的钩子这3个参数有不同的含义,具体请看MSDN。
hMod -- DLL实例句柄(我猜是这样,我不熟悉Win32API编程),当钩子为系统钩子时这个参数是必须的,如果是单个程序或者线程使用的钩子那么可以不用这个值。
dwThreadId -- 线程ID,如果是单个程序或者线程使用的钩子这个参数是必须的。
SetWindowsHookEx返回所设置的钩子的句柄。
BOOL UnhookWindowsHookEx(HHOOK hhk);
hhk -- 所要注销的钩子的句柄
UnhookWindowsHookEx返回注销钩子是否成功。
LRESULT CallNextHookEx(
HHOOK hhk,
int
nCode,
WPARAM wParam,
LPARAM lParam);
hhk -- 起始钩子句柄,如果是在钩子回调函数中调用CallNextHookEx时传的通常是本钩子句柄。
nCode -- 钩子回调函数参数1,具体含义视钩子类型而定。
wParam -- 钩子回调函数参数2,具体含义视钩子类型而定。
lParam -- 钩子回调函数参数3,具体含义视钩子类型而定。
从以上3个函数的简要说明可以看出所用到的参数几乎都是简单的参数,稍微复杂就是回调函数(在C#中用delegate类型来实现,后面会提到),但是最让人头疼的就是DLL的实例句柄,C#制作的DLL并不像C做的DLL有DllMain入口可以接受到DLL实例句柄。
我目前唯一的办法就是使用C做一个系统钩子DLL再让C#调用。底下是DLL的代码。
#define
_WIN32_WINNT 0x0500
#include
<
Windows.h
>
#include
<
WinUser.h
>
#define
Dll_Export __declspec(dllexport)
/**/
/* DLL实例句柄 */
HINSTANCE instance
=
NULL;
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD ul_reason_for_call, LPVOID lpReserved)
{
instance = hInstance;
return TRUE;
}
/**/
/*
*功能:设置系统钩子
*hookType -- 钩子类型
*hookproc -- 钩子回调函数
*/
Dll_Export HHOOK SetHook(
int
hookType,HOOKPROC hookproc)
{
return SetWindowsHookEx(hookType, hookproc, instance, NULL);
}
/**/
/*
*功能:注销钩子
*hook -- 钩子句柄
*/
Dll_Export BOOL UnHook(HHOOK hook)
{
if (hook != NULL)
return UnhookWindowsHookEx(hook);
else
return 0 ;
}
然后在C#项目中创建一个系统钩子类,提供创建钩子和注销钩子的接口,代码如下
public
class
Hook
{
[DllImport( " LowLevelHook.dll " )]
public extern static IntPtr SetHook( int hookType, HookProc hookProc);
[DllImport( " LowLevelHook.dll " )]
public extern staticbool UnHook(IntPtr hook);
}
上面用到的User32.HookProc类型不是.NET框架提供的,是自己定义的一个委托类型,声明如下
public
delegate
int
HookProc(
int
nCode, IntPtr wParam, IntPtr lParam);
现在你的C#程序就可以使用各种系统钩子了,我们目前已经使用的有低级别鼠标钩子和低级别键盘钩子。
应用低级别鼠标钩子时会遇到这样一个问题:
低级别鼠标钩子的回调函数lParam是一个MSLLHOOKSTRUCT结构体指针,C#中怎么获取这个结构体的实例呢?实现代码如下
MSLLHOOKSTRUCT _mstr;
unsafe
{
_mstr =* (User32.MSLLHOOKSTRUCT * )lParam.ToPointer();
}
通过声明非安全代码来获取指针所指对象。
MSLLHOOKSTRUCT结构体的C#定义如下:
[StructLayout(LayoutKind.Sequential)]
public
struct
MSLLHOOKSTRUCT
{
public Point Point;
public int MouseData;
public int Flags;
public int Time;
public int ExtraInfo;
}
PS: 附件--系统钩子的源代码与DLL
补充HookType枚举值,很抱歉一时疏忽忘记写出来,这些值可以在Win32 API 的相关头文件中找到,有LL字样的就是低级钩子:
public enum HookTypes: int
{
WH_JOURNALRECORD = 0 ,
WH_JOURNALPLAYBACK = 1 ,
WH_KEYBOARD = 2 ,
WH_GETMESSAGE = 3 ,
WH_CALLWNDPROC = 4 ,
WH_CBT = 5 ,
WH_SYSMSGFILTER = 6 ,
WH_MOUSE = 7 ,
WH_HARDWARE = 8 ,
WH_DEBUG = 9 ,
WH_SHELL = 10 ,
WH_FOREGROUNDIDLE = 11 ,
WH_CALLWNDPROCRET = 12 ,
WH_KEYBOARD_LL = 13 ,
WH_MOUSE_LL = 14
}
虽然上篇文章已经有但是还是要重点重复说明下CallNextHookEx函数,这个函数的功能是在执行完系统钩子的回调函数后接着调用下一个钩子函数,当设置的钩子是底层键盘或者鼠标钩子时如果不这么做将会时键盘和鼠标"失灵"。
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
CallNextHookEx(IntPtr hookHandle,
int
nCode, IntPtr wParam, IntPtr lParam);
这个函数第一个参数是钩子的句柄,在创建钩子时返回来的那个就是,后面3个参数和钩子回调函数的3个参数一一对应,照传就可以。
因为公司现在装得是VS2005所以我只能先用VS2005做个试验程序了,这个程序功能是锁定鼠标一定时间,需要有.NET Framework 2.0 Runtime才能运行,我会尽快找台装VS2003的电脑做个.NET 1.1的范例的。
可执行文件:http://files.cnblogs.com/BG5SBK/HookTest_NET2_Bin.rar
项目文件:http://files.cnblogs.com/BG5SBK/HookTest_NET2_Source.rar
系统钩子文件:http://files.cnblogs.com/BG5SBK/LowLevelHook.rar
原文地址:http://www.cnblogs.com/bg5sbk/articles/299728.html