它的用处在于:当用户通过鼠标、键盘向windows发出命令的时候,通可HOOK可以截获到这个消息,从而加以处理,或是终止这个消息向底层的传送。
要想设置HOOK还是得用C++,不过我不会,还好,C#可以做一些事情。
在这之前,先要了解一个东西,全局和局部,全局表示在应用程序之外也能截获消息,局部表示只能在应用程序中截获,
程序中,只要调用API就可以了,相关的API有以下几个:
还得要一个HOOK的枚举,它用来表示当前设置的是什么类型的HOOK,
再定义一个变量:IntPtr nextHookPtr; //记录Hook编号
一个委托:
internal delegate IntPtr MyHookProc(int code, IntPtr wparam, IntPtr lparam);
它表示当截获消息时,我想干的事情,
添加一个方法SetHook
/// <summary>
/// 设置HOOK
/// </summary>
private void SetHook()
{
if (nextHookPtr != IntPtr.Zero)//已经勾过了
return;
MyHookProc myhook = new MyHookProc(MyHook);
IntPtr pInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);
//nextHookPtr = SetWindowsHookEx((int)HookType.KeyboardLL, myhook, pInstance, 0);//全局键盘
nextHookPtr = SetWindowsHookEx((int)HookType.Keyboard, myhook, IntPtr.Zero, GetCurrentThreadId());//局部键盘
//nextHookPtr = SetWindowsHookEx((int)HookType.MouseLL, myhook, pInstance, 0);//全局鼠标
//nextHookPtr = SetWindowsHookEx((int)HookType.Mouse, myhook, IntPtr.Zero, GetCurrentThreadId());//局部鼠标
if (nextHookPtr.ToInt32() != 0)
{
label1.Text = "成功";
}
else
{
label1.Text = "失败";
}
}
/// <summary>
/// 卸载HOOK
/// </summary>
private void UnHook()
{
if (nextHookPtr != IntPtr.Zero)
{
UnhookWindowsHookEx(nextHookPtr);
nextHookPtr = IntPtr.Zero;
}
}
在SetHOOK中有一行: MyHookProc myhook = new MyHookProc(MyHook);
这里的MyHook正是对委托的实现:
内容如下:
private IntPtr MyHook(int code, IntPtr wparam, IntPtr lparam)
{
if (code < 0)
return CallNextHookEx(nextHookPtr, code, wparam, lparam);
textBox1.Text = "{" + wparam.ToInt32() + "}";
return (IntPtr)1;
或
Keys k = (Keys)wparam.ToInt32();
textBox1.Text = "{" + k.ToString() + "}";
return (IntPtr)1;
}
这里,在文本框中显示按键的ACCII码,
lparam表示了按键的状态:
string state = string.Empty;
if (lparam.ToInt32() > 0)
{
state = "键盘按下";
}
if (lparam.ToInt32() < 0)
{
state = "键盘抬起";
}
这里的 return CallNextHookEx(nextHookPtr, code, wparam, lparam);
和return IntPtr.Zero;都是一个意思,让系统来处理这条消息,
而 return (IntPtr)1;表示终止这条消息向下传送。
注:
在全局的键盘HOOK中,最好能定义这样一个结构:
public struct KeyMSG
{
public int vkCode;//按键代码
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
这样在实现委托的方法中:
KeyMSG MyKeyboardHookStruct = (KeyMSG)Marshal.PtrToStructure(lparam, typeof(KeyMSG));
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
MessageBox.Show(keyData.ToString());
就可以知道是按的那个键啦。/
当然鼠标也有这样一结构:
public struct MSG
{
public Point p;
public IntPtr HWnd;
public uint wHitTestCode;
public int dwExtraInfo;
}
在鼠标的消息比较麻烦,我这里还列了个枚举,比较有用:
public enum MouseType
{
None = 512,
LeftDown = 513,
LeftUp = 514,
RightDown = 516,
RightUp = 517,
MiddleDown = 519,
MiddleUp = 520,
MiddleScroll = 522,
//512,未按键
//513,左键下
//514,左键上
//516,右键下
//517,右键上
//519,中键下
//520,中键上
//522,滚动中键
}
这样,通过以下可获得鼠标状态:
int w = wparam.ToInt32();
MouseType ms = MouseType.None;
switch (w)
{
case 513:
ms = MouseType.LeftDown;
break;
case 514:
ms = MouseType.LeftUp;
break;
case 516:
ms = MouseType.RightDown;
break;
case 517:
ms = MouseType.RightUp;
break;
case 519:
ms = MouseType.MiddleDown;
break;
case 520:
ms = MouseType.MiddleUp;
break;
case 522:
ms = MouseType.MiddleScroll;
break;
}
再通过以下可获得鼠标的坐标:MSG m = (MSG)Marshal.PtrToStructure(lparam, typeof(MSG))
listBox1.Items.Add(ms.ToString() + " X:" + m.p.X.ToString() + ",Y:" + m.p.Y.ToString());
不过由于鼠标产生的消息太多了,所以程序会经常驻卡死,用得少,呵呵。
另外,当设置为全局的HOOK的时候,不能直接运行来调试,而要打开应用程序才可以看到。
找到了一个别人写的类,很不错的,把那个委托的过程都写成了事件,这样用户就完全可以操作了: