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