最近因为一些原因,学习了一下HOOK这个东西。
它的用处在于:当用户通过鼠标、键盘向windows发出命令的时候,通可HOOK可以截获到这个消息,从而加以处理,或是终止这个消息向底层的传送。
要想设置HOOK还是得用C++,不过我不会,还好,C#可以做一些事情。
在这之前,先要了解一个东西,全局和局部,全局表示在应用程序之外也能截获消息,局部表示只能在应用程序中截获,
程序中,只要调用API就可以了,相关的API有以下几个:
[DllImport(
"
kernel32.dll
"
)]
static
extern
int
GetCurrentThreadId();
//
取得当前线程编号的API
[DllImport(
"
user32.dll
"
, CharSet
=
CharSet.Auto, CallingConvention
=
CallingConvention.StdCall)]
internal
extern
static
void
UnhookWindowsHookEx(IntPtr handle);
//
取消Hook
[DllImport(
"
user32.dll
"
, CharSet
=
CharSet.Auto, CallingConvention
=
CallingConvention.StdCall)]
internal
extern
static
IntPtr SetWindowsHookEx(
int
idHook, HookProc lpfn, IntPtr hinstance,
int
threadID);
//
设置Hook
[DllImport(
"
user32.dll
"
, CharSet
=
CharSet.Auto, CallingConvention
=
CallingConvention.StdCall)]
internal
extern
static
IntPtr CallNextHookEx(IntPtr handle,
int
code, IntPtr wparam, IntPtr lparam);
//
下一个Hook
还得要一个HOOK的枚举,它用来表示当前设置的是什么类型的HOOK,
internal
enum
HookType
//
枚举,钩子的类型
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
MsgFilter = -1,
JournalRecord = 0,
JournalPlayback = 1,
Keyboard = 2,
GetMessage = 3,
CallWndProc = 4,
CBT = 5,
SysMsgFilter = 6,
Mouse = 7,
Hardware = 8,
Debug = 9,
Shell = 10,
ForegroundIdle = 11,
CallWndProcRet = 12,
KeyboardLL = 13,
MouseLL = 14,
}
再定义一个变量:
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的时候,不能直接运行来调试,而要打开应用程序才可以看到。
找到了一个别人写的类,很不错的,把那个委托的过程都写成了事件,这样用户就完全可以操作了:
public
class
KeyBordHook
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYDOWN = 0x104;
private const int WM_SYSKEYUP = 0x105;
![](/Images/OutliningIndicators/InBlock.gif)
//全局的事件
public event KeyEventHandler OnKeyDownEvent;
public event KeyEventHandler OnKeyUpEvent;
public event KeyPressEventHandler OnKeyPressEvent;
static int hKeyboardHook = 0; //键盘钩子句柄
//鼠标常量
public const int WH_KEYBOARD_LL = 13; //keyboard hook constant
HookProc KeyboardHookProcedure; //声明键盘钩子事件类型.
//声明键盘钩子的封送结构类型
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public int vkCode; //表示一个在1到254间的虚似键盘码
public int scanCode; //表示硬件扫描码
public int flags;
public int time;
public int dwExtraInfo;
}
//装置钩子的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//卸下钩子的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
![](/Images/OutliningIndicators/InBlock.gif)
//下一个钩挂的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
[DllImport("user32 ")]
public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);
[DllImport("user32 ")]
public static extern int GetKeyboardState(byte[] pbKeyState);
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 墨认的构造函数构造当前类的实例并自动的运行起来.
/// </summary>
public KeyBordHook()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Start();
}
![](/Images/OutliningIndicators/InBlock.gif)
//析构函数.
~KeyBordHook()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Stop();
}
![](/Images/OutliningIndicators/InBlock.gif)
public void Start()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//安装键盘钩子
if (hKeyboardHook == 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule), 0);
if (hKeyboardHook == 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Stop();
throw new Exception("SetWindowsHookEx ist failed. ");
}
}
}
![](/Images/OutliningIndicators/InBlock.gif)
public void Stop()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
bool retKeyboard = true;
![](/Images/OutliningIndicators/InBlock.gif)
if (hKeyboardHook != 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
//如果卸下钩子失败
if (!(retKeyboard)) throw new Exception("UnhookWindowsHookEx failed. ");
}
![](/Images/OutliningIndicators/InBlock.gif)
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
![](/Images/OutliningIndicators/InBlock.gif)
//引发OnKeyDownEvent
if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
OnKeyDownEvent(this, e);
}
![](/Images/OutliningIndicators/InBlock.gif)
//引发OnKeyPressEvent
if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (ToAscii(MyKeyboardHookStruct.vkCode,
MyKeyboardHookStruct.scanCode,
keyState,
inBuffer,
MyKeyboardHookStruct.flags) == 1)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
OnKeyPressEvent(this, e);
}
}
![](/Images/OutliningIndicators/InBlock.gif)
//引发OnKeyUpEvent
if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
OnKeyUpEvent(this, e);
}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
}
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**/
/// <summary>
/// 这个类可以让你得到一个在运行中程序的所有鼠标事件
/// 并且引发一个带MouseEventArgs参数的.NET鼠标事件以便你很容易使用这些信息
/// </summary>
public
class
MouseHook
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
private const int WM_MOUSEMOVE = 0x200;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_RBUTTONDOWN = 0x204;
private const int WM_MBUTTONDOWN = 0x207;
private const int WM_LBUTTONUP = 0x202;
private const int WM_RBUTTONUP = 0x205;
private const int WM_MBUTTONUP = 0x208;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDBLCLK = 0x209;
//全局的事件
public event MouseEventHandler OnMouseActivity;
static int hMouseHook = 0; //鼠标钩子句柄
//鼠标常量
public const int WH_MOUSE_LL = 14; //mouse hook constant
HookProc MouseHookProcedure; //声明鼠标钩子事件类型.
//声明一个Point的封送类型
[StructLayout(LayoutKind.Sequential)]
public class POINT
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public int x;
public int y;
}
//声明鼠标钩子的封送结构类型
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public POINT pt;
public int hWnd;
public int wHitTestCode;
public int dwExtraInfo;
}
//装置钩子的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//卸下钩子的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
![](/Images/OutliningIndicators/InBlock.gif)
//下一个钩挂的函数
[DllImport("user32.dll ", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 墨认的构造函数构造当前类的实例.
/// </summary>
public MouseHook()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Start();
}
//析构函数.
~MouseHook()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Stop();
}
public void Start()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//安装鼠标钩子
if (hMouseHook == 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//生成一个HookProc的实例.
MouseHookProcedure = new HookProc(MouseHookProc);
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule), 0);
//如果装置失败停止钩子
if (hMouseHook == 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Stop();
throw new Exception("SetWindowsHookEx failed. ");
}
}
}
public void Stop()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
bool retMouse = true;
if (hMouseHook != 0)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
retMouse = UnhookWindowsHookEx(hMouseHook);
hMouseHook = 0;
}
![](/Images/OutliningIndicators/InBlock.gif)
//如果卸下钩子失败
if (!(retMouse)) throw new Exception("UnhookWindowsHookEx failed. ");
}
private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//如果正常运行并且用户要监听鼠标的消息
if ((nCode >= 0) && (OnMouseActivity != null))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
MouseButtons button = MouseButtons.None;
int clickCount = 0;
switch (wParam)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
case WM_LBUTTONDOWN:
button = MouseButtons.Left;
clickCount = 1;
break;
case WM_LBUTTONUP:
button = MouseButtons.Left;
clickCount = 1;
break;
case WM_LBUTTONDBLCLK:
button = MouseButtons.Left;
clickCount = 2;
break;
case WM_RBUTTONDOWN:
button = MouseButtons.Right;
clickCount = 1;
break;
case WM_RBUTTONUP:
button = MouseButtons.Right;
clickCount = 1;
break;
case WM_RBUTTONDBLCLK:
button = MouseButtons.Right;
clickCount = 2;
break;
}
//从回调函数中得到鼠标的信息
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
OnMouseActivity(this, e);
}
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
}