最近因为一些原因,学习了一下HOOK这个东西。
它的用处在于:当用户通过鼠标、键盘向windows发出命令的时候,通可HOOK可以截获到这个消息,从而加以处理,或是终止这个消息向底层的传送。
要想设置HOOK还是得用C++,不过我不会,还好,C#可以做一些事情。
在这之前,先要了解一个东西,全局和局部,全局表示在应用程序之外也能截获消息,局部表示只能在应用程序中截获,
程序中,只要调用API就可以了,相关的API有以下几个:

C# 使用HOOK 小记[DllImport("kernel32.dll")]
C# 使用HOOK 小记        
static extern int GetCurrentThreadId(); //取得当前线程编号的API 
C# 使用HOOK 小记

C# 使用HOOK 小记        [DllImport(
"user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
C# 使用HOOK 小记        
internal extern static void UnhookWindowsHookEx(IntPtr handle); //取消Hook 
C# 使用HOOK 小记

C# 使用HOOK 小记        [DllImport(
"user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
C# 使用HOOK 小记        
internal extern static IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hinstance, int threadID); //设置Hook
C# 使用HOOK 小记
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
C# 使用HOOK 小记        
internal extern static IntPtr CallNextHookEx(IntPtr handle, int code, IntPtr wparam, IntPtr lparam); //下一个Hook

还得要一个HOOK的枚举,它用来表示当前设置的是什么类型的HOOK,
C# 使用HOOK 小记internal enum HookType //枚举,钩子的类型 
        }

再定义一个变量: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的时候,不能直接运行来调试,而要打开应用程序才可以看到。

找到了一个别人写的类,很不错的,把那个委托的过程都写成了事件,这样用户就完全可以操作了:
C# 使用HOOK 小记 public class KeyBordHook
    }




相关文章:

  • 2021-04-07
  • 2021-06-19
  • 2021-12-02
  • 2022-02-18
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-12-19
  • 2021-09-20
  • 2021-06-01
  • 2022-12-23
  • 2021-12-16
相关资源
相似解决方案