C# 使用HOOK 小记

最近因为一些原因,学习了一下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 //枚举,钩子的类型 
        {
            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
    
{
        
private const int WM_KEYDOWN = 0x100;
        
private const int WM_KEYUP = 0x101;
        
private const int WM_SYSKEYDOWN = 0x104;
        
private const int WM_SYSKEYUP = 0x105;

        
//全局的事件 
        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
        
{
            
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);

        
//下一个钩挂的函数 
        [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);

        
///   <summary> 
        
///   墨认的构造函数构造当前类的实例并自动的运行起来. 
        
///   </summary> 

        public KeyBordHook()
        
{
            Start();
        }


        
//析构函数. 
        ~KeyBordHook()
        
{
            Stop();
        }


        
public void Start()
        
{
            
//安装键盘钩子   
            if (hKeyboardHook == 0)
            
{
                KeyboardHookProcedure 
= new HookProc(KeyboardHookProc);
                hKeyboardHook 
= SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule), 0);
                
if (hKeyboardHook == 0)
                
{
                    Stop();
                    
throw new Exception("SetWindowsHookEx   ist   failed. ");
                }

            }

        }


        
public void Stop()
        
{
            
bool retKeyboard = true;

            
if (hKeyboardHook != 0)
            
{
                retKeyboard 
= UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook 
= 0;
            }

            
//如果卸下钩子失败 
            if (!(retKeyboard)) throw new Exception("UnhookWindowsHookEx   failed. ");
        }


        
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        
{
            
if ((nCode >= 0&& (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
            
{
                KeyboardHookStruct MyKeyboardHookStruct 
= (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

                
//引发OnKeyDownEvent 
                if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
                
{
                    Keys keyData 
= (Keys)MyKeyboardHookStruct.vkCode;
                    KeyEventArgs e 
= new KeyEventArgs(keyData);
                    OnKeyDownEvent(
this, e);
                }


                
//引发OnKeyPressEvent 
                if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
                
{
                    
byte[] keyState = new byte[256];
                    GetKeyboardState(keyState);
                    
byte[] inBuffer = new byte[2];
                    
if (ToAscii(MyKeyboardHookStruct.vkCode,
                      MyKeyboardHookStruct.scanCode,
                      keyState,
                      inBuffer,
                      MyKeyboardHookStruct.flags) 
== 1)
                    
{
                        KeyPressEventArgs e 
= new KeyPressEventArgs((char)inBuffer[0]);
                        OnKeyPressEvent(
this, e);
                    }

                }


                
//引发OnKeyUpEvent 
                if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
                
{
                    Keys keyData 
= (Keys)MyKeyboardHookStruct.vkCode;
                    KeyEventArgs e 
= new KeyEventArgs(keyData);
                    OnKeyUpEvent(
this, e);
                }

            }

            
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
        }

    }



    
///   <summary> 
    
///   这个类可以让你得到一个在运行中程序的所有鼠标事件 
    
///   并且引发一个带MouseEventArgs参数的.NET鼠标事件以便你很容易使用这些信息 
    
///   </summary> 

    public class MouseHook
    
{
        
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
        
{
            
public int x;
            
public int y;
        }

        
//声明鼠标钩子的封送结构类型 
        [StructLayout(LayoutKind.Sequential)]
        
public class MouseHookStruct
        
{
            
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);

        
//下一个钩挂的函数 
        [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);
        
///   <summary> 
        
///   墨认的构造函数构造当前类的实例. 
        
///   </summary> 

        public MouseHook()
        
{
            Start();
        }

        
//析构函数. 
        ~MouseHook()
        
{
            Stop();
        }

        
public void Start()
        
{
            
//安装鼠标钩子 
            if (hMouseHook == 0)
            
{
                
//生成一个HookProc的实例. 
                MouseHookProcedure = new HookProc(MouseHookProc);
                hMouseHook 
= SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule), 0);
                
//如果装置失败停止钩子 
                if (hMouseHook == 0)
                
{
                    Stop();
                    
throw new Exception("SetWindowsHookEx failed. ");
                }

            }

        }

        
public void Stop()
        
{
            
bool retMouse = true;
            
if (hMouseHook != 0)
            
{
                retMouse 
= UnhookWindowsHookEx(hMouseHook);
                hMouseHook 
= 0;
            }


            
//如果卸下钩子失败 
            if (!(retMouse)) throw new Exception("UnhookWindowsHookEx   failed. ");
        }

        
private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
        
{
            
//如果正常运行并且用户要监听鼠标的消息 
            if ((nCode >= 0&& (OnMouseActivity != null))
            
{
                MouseButtons button 
= MouseButtons.None;
                
int clickCount = 0;
                
switch (wParam)
                
{
                    
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);
        }

    }




posted @ 2008-03-17 17:30  point.deng  阅读(1398)  评论(1编辑  收藏  举报