欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Net实现钩子函数(Hook)以及通过SendMessage实现自动点击按钮和给文本框赋值

程序员文章站 2022-04-10 14:38:33
1.实现钩子函数 钩子(Hook)的实现需要三个主要的函数和一个委托 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static exte ......

1.实现钩子函数

钩子(hook)的实现需要三个主要的函数和一个委托

[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)]
private 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);//用于处理hook住的消息

当我们在执行一个操作的时候,首先不是由我们的窗体获得消息,而是系统获得,然后系统再把消息发送到对应的窗体,hook就是在窗体获取到信息之前抓住信息,然后对信息进行处理,然后可以传递给船体继续执行,或者就不传递给窗体

当在hookproc处理消息的时候,如果return 1,那么消息就会被截断,不会再传递到目标窗口,如果return的是callnexthookex那么就会继续调用下一个钩子,如果下面没有钩子了,那么消息就会被传递到目标窗体进行处理

setwindowshookex第一个参数是需要勾住的消息类型,总共14种消息类型,如下

public const int wh_journalrecord = 0;
public const int constwh_journalplayback = 1;
public const int wh_keyboard = 2;
public const int wh_getmessage = 3;
public const int wh_callwndproc = 4;
public const int wh_cbt = 5;
public const int wh_sysmsgfilter = 6;
public const int wh_mouse = 7;
public const int wh_hardware = 8;
public const int wh_debug = 9;
public const int wh_shell = 10;
public const int wh_foregroundidle = 11;
public const int wh_callwndprocret = 12;
public const int wh_keyboard_ll = 13;
public const int wh_mouse_ll = 14;

第二个参数就是hookproc委托,用于对钩住的消息进行处理,

第三个参数是需要钩住的实例的句柄,最后一个是钩住的线程,如果是0则是全局钩住

返回值为抓住的钩子的id

unhookwindowshookex卸载掉钩子,参数为上面返回的id

 

辅助函数

[dllimport("kernel32.dll")]

public static extern intptr getmodulehandle(string name);//根据模块名称获取到对应的句柄

[dllimport("user32.dll", entrypoint = "findwindow")]

private extern static intptr findwindow(string lpclassname, string lpwindowname);//查询一个窗体

[dllimport("user32.dll", entrypoint = "findwindowex")]

private static extern intptr findwindowex(intptr hwndparent, intptr hwndchildafter, string lpclassname, string lpwindowname);//获取窗体中的所有子窗体(文本框,按钮等,都属于窗体)

 

        [dllimport("user32.dll")]

        public static extern int enumchildwindows(intptr hwndparent, callback lpfn, int lparam);//枚举窗体中的所有子窗体

public delegate bool callback(intptr hwnd, int lparam);

此委托是enumchildwindows的回调函数,用于遍历的时候对窗口进行处理

 

根据module的名字获取到对应的句柄setwindowshookex的第三个参数可以使用这个函数来获得。

下面是一个示例程序,设置一个全局钩子,作用是,如果输入的字符是小写字母,则直接转换为大写字母。

1.1  hookproc的方法实现

  private int messagehandle(int ncode, int32 wparam, intptr lparam)

        {

            if (0x100 == wparam || 0x101 == wparam)  //如果按键为按下状态,如果没有这句判断,则内部代码会执行两遍,一遍是keydown一遍是keyup

            {

                kbdllhookstruct ks = (kbdllhookstruct)marshal.ptrtostructure(lparam, typeof(kbdllhookstruct));

               //将所有的小写字母直接加1

                if (ks.vkcode >= 65 && ks.vkcode <= 90)

                {

                    string cupper = convert.tochar(ks.vkcode).tostring().toupper();

                    sendmessage(txthandle, 0x0c, intptr.zero, cupper);

                }

            }

            return callnexthookex(result, ncode, 0, lparam);

 

        }

1.2 kbdllhookstruct结构体(这个结构体因为不同的钩子内容会不一样)

public struct kbdllhookstruct

    {

        public int vkcode;

        public int scancode;

        public int flags;

        public int time;

        public intptr dwextrainfo;

    }

 

1.3设置钩子和卸载钩子(两个按钮的事件)

  private void btninstallhook_click(object sender, eventargs e)

        {

            hookproc hproc = new hookproc(messagehandle);

            intptr cinstance = getmodulehandle(process.getcurrentprocess().mainmodule.modulename);

            result = setwindowshookex(hookhelper.wh_keyboard_ll, hproc, cinstance, 0);

        }

 

        private void btnunhook_click(object sender, eventargs e)

        {

            unhookwindowshookex(result);

        }

辅助方法:为了获取到窗体中的文本框的句柄

//枚举窗体中的子窗体的回调函数

        private bool enumwindow(intptr hwnd, int lparam)

        {

            stringbuilder sb=new stringbuilder();

            getwindowtext(hwnd, sb, 10);

            if (sb.tostring() == "hooktest")

            {

                txthandle = hwnd;

            }

            return true;

        }

 

 

2.sendmessage的使用

可以使用sendmessage模拟给发送一条系统消息

[dllimport("user32.dll", entrypoint = "sendmessage")]

        private static extern int sendmessage(intptr hwnd, int wmsg, intptr wparam, intptr lparam);

 

        [dllimport("user32.dll", entrypoint = "sendmessage")]

        private static extern int sendmessage(intptr hwnd, int msg, intptr wparam, string lparam);//发送消息,此重载方法可以直接给文本框赋值

 

下面是一个自动点击按钮和自动给文本框赋值的示例

 

   private void btntest_click(object sender, eventargs e)

        {

            #region 自动点击按钮

            //intptr cprocess = findwindow(null, "测试hook");

            //winhandle = findwindowex(cprocess, intptr.zero, null, "点击显示界面");

            ////sendmessage(winhandle, 0xf5, 0, 0);//0xf5 bm_click 按钮单击对应的消息--经过测试,直接使用0xf5无法实现点击按钮的功能    

            ////测试结果发现,如果想要实现单击按钮的功能,必须先按下鼠标左键,再抬起鼠标左键

            //sendmessage(winhandle, 0x201, intptr.zero, intptr.zero);//0x201 wm_lbuttondown 按下鼠标左键对应的消息

            //sendmessage(winhandle, 0x202, intptr.zero, intptr.zero);//0x201 wm_lbuttonup 抬起鼠标左键对应的消息

            #endregion

 

            #region 自动输入文本

            //intptr cprocess = findwindow(null, "test.txt - 记事本");

            //winhandle = findwindowex(cprocess, intptr.zero, null, "");

 

            //intptr cprocess = findwindow(null, "测试hook");

            //winhandle = findwindowex(cprocess, intptr.zero, null, null);

            ////winhandle = new intptr(0xe10f2);//这种方式是先通过spy++找到控件的句柄,然后再使用这个句柄进行数据交互(此方法每次重启窗体,对应的句柄都会发生变化)

            //sendmessage(txthandle, 0x0c, intptr.zero, "abcdefghijklmn");//0x0c wm_settext 给窗体设置文本     

            #endregion

        }

 源代码:https://files.cnblogs.com/files/ckym/hooktest.rar