Net實現鈎子函數(Hook)以及通過SendMessage實現自動點擊按鈕和給文本框賦值


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM