最近学着编写钩子,花了好几天终于有点头绪,总结一下写在下面:
1、使用C#编写局部钩子
C#编写全局钩子时,我用的时使用C#编写一个dll文件,再使用C#窗体调用该dll文件(不是说全局钩子只能使用DLL么),结果很多消息检测不到,甚至有些钩子就不能成功,所以目前只能用它做局部钩子:
声明API函数:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int SetWindowsHookEx(int idHook,HookProc lpfn,IntPtr hMod,int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(int idHook,int nCode,int wParam,IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
//定义回调函数委托
private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
//声明变量
private const int WH_CBT = 5;
private const int HCBT_MOVESIZE = 0;
private const int HCBT_MINMAX =1;
//设置钩子
public void sethook()
{
CBTProc = new HookProc(CBTProchook);
hmenuhook = SetWindowsHookEx(WH_CBT,CBTProcIntPtr.Zero, GetCurrentThreadId());
}
注意:IntPtr hMod为dll的实例句柄,当是局部钩子时为NULL,int dwThreadId为当前线程ID,如果是全局钩子就设为0;对于C#获取DLL的实例句柄:(IntPtr)Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),要增加using System.Reflection;命名空间
//编写回调函数
private int CBTProchook(int nCode, int wParam, IntPtr lParam)
{
MessageBox.Show("开始");
if (nCode == HC_ACTION)
{
MessageBox.Show("asf");
}
return CallNextHookEx(hmenuhook, nCode, wParam, lParam);
}
//取消钩子
UnhookWindowsHookEx(hmenuhook);
2、使用C++和C#制作全局钩子
使用C++制作DLL,再用C#调用DLL中的方法,我使用的是VS2005,新建win32 dll空文件,添加hook.cpp和hook.def文件,在hook.cpp文件中添加代码:
#include <Windows.h>
HOOK g_CBTmsg;
//声明共享变量
#pragma data_seg("mydata")
HWND lms_hwnd=NULL;
HWND menu_hwnd=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:mydata,RWS")
//设置钩子 其中接收两个句柄参数
void sethook(HWND lmshwnd,HWND menuhwnd)
{
lms_hwnd=lmshwnd;
menu_hwnd=menuhwnd;
g_CBTmsg=SetWindowsHookEx(WH_CBT,CBTProc,GetModuleHandle(L"hook"),0);
} //GetModuleHandle(L"hook") 获得当前的即dll实例句柄
//设置回调函数
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam,LPARAM lParam)
{
... ...具体参见MSDN
}
填写hook.def
LIBRARY "hook" //dll名称
EXPORTS
sethook @2 //输出函数
VS2005与VS6.0有点不同,不会自己读取def文件,在工程属性>linker>input>modile definition file中输入hook.def,编译即可生成dll文件和lib文件。注意在debug和Release中都要添加。
使用C#调用c++做的dll,我是显式调用的:
[DllImport("hook.dll", EntryPoint = "sethook", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
public static extern int sethook(IntPtr lmshwnd, IntPtr menuhwnd);
再调用函数即可。
c#根据进程名获取主窗体的句柄:
Process[] LmsProcess = Process.GetProcessesByName("notepad");
LmsProcess[0].MainWindowHandle;
该方法非常使用,但不知道在MFC或API函数中怎么实现,还请各位高手指点。急......................
sethook(lms_hwnd, this.Handle); 给dll中的方法传递两句柄参数