键盘钩子在C#中的规划

线程勾子用于监视内定线程的轩然大波消息。线程勾子一般在此时此刻线程也许当前线程派生的线程内。

 

    public class PrivateHook : hook
    {
        public PrivateHook(HOOKPROC proc)
            : base(proc, HookType.WH_KEYBOARD)
        { }

而System.Threading.Thread.CurrentThread.ManagedThreadId再次来到的是当做七个ManagedThread在.Net
CLTiggo中的ThreadId,所以那和Windows的ThreadId是完全分歧的。

 

最后,欢迎朋友们展开指正,多谢!

几点表明:

        [StructLayout(LayoutKind.Sequential)]
        public class KeyBoardHookStruct
        {
            public int vkCode;//表明一个在1到254间的虚拟键盘码
            public int scanCode;//表示硬件扫描码
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

 

        public abstract int SetWindowsHookEx();
        public virtual void UnhookWindowsHookEx()
        {
            bool retKeyboard = true;
            if (hHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hHook);
                hHook = 0;
            }
            if (!retKeyboard)
            {
                throw new Exception(“UnhookWindowsHookEx failed.”);
            }
        }
    }

图片 1图片 2代码

一 、AppDomain.GetCurrentThreadId()在.net
2.0中过时了,VS二零零七和VS二零一零警示那几个主意已经过时,建议使用System.Threading.Thread.CurrentThread.ManagedThreadId,但实质上那七个值是差异等的。AppDomain.GetCurrentThreadId()的实际调用Win32
API,其回到的是该线程在Windows中的ThreadId,即同那个等价:

上述所说的钩子程与线程相关联是指在一钩子链表中发放该线程的音信还要发送给钩子子程,且被钩子子程先处理。

函数成功则赶回钩子子程的句柄,战败重临NULL。

 

 Best Reagards,

 叁 、系统钩子和线程钩子:

        /// <summary>
        /// 钩子处理委托
        /// </summary>
        public HOOKPROC proc;
        /// <summary>
        /// 钩子类型
        /// </summary>
        public HookType type;
        /// <summary>
        /// 钩子的句柄
        /// </summary>
        public int hHook = 0;

    
趁空闲的时日,对键盘钩子进行了上学,通过C#这门语言来计划和达成:上边是本人安插的类图:

 public abstract class hook
    {
        //设置钩子
        [DllImport(“user32.dll”, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(HookType idHook, HOOKPROC lpfn, IntPtr hInstance, int threadId);
        //抽调钩子
        [DllImport(“user32.dll”, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

图片 3图片 4Hook基类完结

 

图片 5图片 6大局钩子和民用钩子完成

二 、使用API函数SetWindowsHookEx()把2个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的起来安装Hook子程。当钦赐项目标Hook监视的轩然大波时有发生时,系统就调用与那个Hook关联的Hook链的启幕的Hook子程。每二个Hook链中的Hook子程都决定是或不是把这几个事件传递到下二个Hook子程。Hook子程传递事件到下二个Hook子程要求调用CallNextHookEx函数.函数签名如下:

Charles Chen

 

        public int MyKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
                if (kbh.vkCode == (int)Keys.S && (int)Control.ModifierKeys == (int)Keys.Control)  // 截获F8
                {
                    MessageBox.Show(“快捷键已拦截!无法保存!”);
                    return 1;
                }
                if ((int)Control.ModifierKeys == (int)Keys.Delete && (int)Control.ModifierKeys == (int)Keys.Alt && (int)Control.ModifierKeys == (int)Keys.Control)
                {
                    MessageBox.Show(“捕捉到Ctrl+Alt+Delete”);
                    return 1;
                }
                if (kbh.vkCode == (int)Keys.Y
                   && (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt)  //截获Ctrl+Alt+Y
                {
                    About msg = new About();
                    msg.Show();
                    MessageBox.Show(“无法整个保留!”);
                    return 1;
                }
                if (kbh.vkCode == (int)Keys.A)
                {
                    MessageBox.Show(“A”);
                    this.label1.Text += “A”;
                }
                if (kbh.vkCode == (int)Keys.B)
                {
                    MessageBox.Show(“B”);
                    this.label1.Text += “B”;
                }
                if (kbh.vkCode == (int)Keys.Enter)
                {
                    this.label1.Text = “执行成功!”;
                }
                if (kbh.vkCode == (int)Keys.Back)
                {
                    this.label1.Text = this.label1.Text.Remove(this.label1.Text.Length – 1);
                }
                if (kbh.vkCode == (int)Keys.D1)
                {
                    this.label1.Text += “1”;
                }
            }
            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }

public int MyKeyboardProc(int nCode, int wParm, int lParam)
{
            MessageBox.Show(“你曾经按下了按钮!”);
            return 0;
}

(2)对同一事件音信可设置五个勾子处理进度,那些勾子处理进度形成了勾子链。当前勾子处理完毕后应把勾子音信传送给下一个勾子函数。

   public delegate int HOOKPROC(int nCode, int wParam, int lParam);
   public enum HookType
    {
        WH_KEYBOALANDD = 2,//私有钩子
        WH_KEYBOARD_LL = 13//全局钩子
    }

HHOOK SetWindowsHookEx( 
     int idHook,      // 钩子的品种,即它处理的新闻类型
     HOOKPROC lpfn,   // 钩子子程的地方指针。假使dwThreadId参数为0
               // 或是四个由别的进度创立的线程的标识,
               // lpfn必须指向DLL中的钩子子程。
               // 除此以外,lpfn能够本着当前进度的一段钩子子程代码。
               // 钩子函数的入口地址,当钩子钩到其余消息后便调用这么些函数。
     HINSTANCE hMod,  // 应用程序实例的句柄。标识包含lpfn所指的子程的
DLL。
               // 就算dwThreadId 标识当前历程成立的一个线程,
               // 而且子程代码位于当前经过,hMod必须为NULL。
               // 能够相当粗略的设定其为本应用程序的实例句柄。
     DWOLacrosseD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
               // 假设为0,钩子子程与富有的线程关联,即为全局钩子。
                 ); 

 

运作后主界面效果如下图所示:

 

系统勾子监视系统中的全部线程的轩然大波新闻。因为系统勾子会潜移默化系统中装有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL)
中。系统活动将包括”钩子回调函数”的DLL映射到受钩子函数影响的有着进度的地方空间中,即将这几个DLL注入了那1个经过。

图片 7图片 8这里能够添加本身想要的新闻处理

 

四 、钩子处理函数:public int MyKeyboardProc(int nCode, int wParm, int lParam)

图片 9

 

 

[DllImport(“user32.dll”, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

SetWindowsHookEx()函数的末尾三个参数决定了此钩子是系统钩子如故线程钩子。

 

 

 //键盘Hook结构函数(详情查看msdn的Platform SDK中的KBDLLHOOKSTRUCT结构)

 图片 10

 

能够拉长自身想要的消息处理(如发邮件,扩张文件等)

小心:要是回去1,则甘休新闻,那一个消息到此甘休,不再传递。借使重临0或调用CallNextHookEx函数则信息出了那一个钩子继续往下传递,也正是传给音信确实的接受者.

[DllImport(“kernel32”)]
public static extern int GetCurrentThreadId();

        public hook(HOOKPROC proc, HookType type)
        {
            this.proc = proc;
            this.type = type;
        }

伍 、钩子函数执行两次的消除办法:http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html

private hook hook = null;
private void btnOpen_Click(object sender, EventArgs e)
{
            if (this.comboBox1.SelectedIndex == 0)
                hook = new PublicHook(MyKeyboardProc);
            else
                hook = new PrivateHook(MyKeyboardProc);
            int hHook = hook.SetWindowsHookEx();
            if (hHook == 0)
            {
                MessageBox.Show(“设置钩子退步!”);
            }
}
private void btnClose_Click(object sender, EventArgs e)
{
            try
            {
                if (hook != null)
                    hook.UnhookWindowsHookEx();
            }
            catch
            {
                MessageBox.Show(“关闭钩子战败!”);
            }
}

图片 11图片 12主界面调用实现

 

        public override int SetWindowsHookEx()
        {
            if (hHook == 0)
                hHook = SetWindowsHookEx(this.type, this.proc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
            return hHook;
        }
    }

 public class PublicHook : hook
    {
        public PublicHook(HOOKPROC proc)
            : base(proc, HookType.WH_KEYBOARD_LL)
        { }

        public override int SetWindowsHookEx()
        {
            if (hHook == 0)
                hHook = SetWindowsHookEx(this.type, this.proc, IntPtr.Zero, GetCurrentThreadId());
            return hHook;
        }
    }

键盘钩子包涵两类:全局钩子和村办钩子,那里本身分成七个类来统一筹划:

 

上边备注一下在应用进程中某些类库表达及注意事项:

(3)勾子尤其是系统勾子会花费音讯处理时间,下跌系统性子。只有在须要的时候才安装勾子,在采用完毕后要立时卸载。

        [DllImport(“kernel32”)]
        public static extern int GetCurrentThreadId();

(1)假设对于同一事件(如鼠标音信)既安装了线程勾子又安装了系统勾子,那么系统会自行先调用线程勾子,然后调用系统勾子。

相关文章