很多時候,編寫程序模擬鼠標和鍵盤操作可以方便的實現你需要的功能,而不需要對方程序為你開放接口。比如,操作飛信定時發(fā)送短信等。我之前開發(fā)過飛信耗子,用的是對飛信協議進行抓包,然后分析協議,進而模擬協議的執(zhí)行,開發(fā)出了客戶端,與移動服務器進行通信,但是這有一些缺點。如果移動的服務器對接口進行變更,我所編寫的客戶端也要進行相應的升級。如果服務器的協議進行了更改,甚至個人編寫的這種第三方客戶端需要重寫。而我個人也沒有這個時間和精力,或者說沒有足夠的利益支撐我繼續(xù)去重構飛信耗子。因此,這款還算優(yōu)秀的軟件,現在就束之高閣了,我自己也覺得遺憾。上周,某項目驗收,需要修改界面,但是零時找不到源碼了。我在兩三個小時內要解決這個問題,時間緊迫。我突然想起室友以前做過模擬鼠標鍵盤去發(fā)送飛信消息的小程序。于是我趕緊電話咨詢了一下。然后掌握了這個技巧,按時解決了問題。我覺得這個技巧還是很有用的,現總結如下:
首先,引入如下三個API接口:
[DllImport("user32.dll")]
public static extern IntPtrFindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
privatestatic extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam,string lParam);
[DllImport("User32.dll ")]
publicstatic extern IntPtr FindWindowEx(IntPtr parent, IntPtr childe, stringstrclass, string FrmText);
第一個與第三個是用于查找窗口句柄的,凡運行于Windows上的窗口,都具有句柄。窗口上的文本框,按鈕之類的,也有其句柄(可看作子窗口句柄)。這些句柄的類型可以通過Spy++進行查詢。比如C語言編寫的程序中,文本框的句柄類型一般為“EDIT”,C#寫的程序則不是,可以具體去查。第二個接口則是用于向窗口發(fā)送各種消息,比如向文本框發(fā)送字符串,或者向按鈕發(fā)送按下與彈起的消息等。詳細解釋如下:
IntPtr hwnd =FindWindow(null, "無標題 - 記事本");這是用于查找操作系統(tǒng)中打開的窗口中標題名為無標題 - 記事本的窗口。
第一個參數是此窗口的類型。這兩個參數知道一個即可,另一個可以填null。
但是如果是用窗口類型查找,則可能只能得到其中的一個窗口。因 此通過標題進行查找是非常方便的。 IntPtr htextbox =FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null);
這個函數用于獲得窗口中子窗口的句柄,子窗口指的其實就是窗口中的各種控件。第一個參數是父窗口的句柄,第二個參數指示獲得的是同一類型中的第幾個子窗口。填IntPtr.Zero則表示獲得第一個子窗口。第三個參數表示你需要找的子窗口的類型,第四個參數一般為null。如果一個窗口中有兩個文本框,那么可以用如下操作獲得第二個文本框的句柄。
IntPtr htextbox = FindWindowEx(hwnd,IntPtr.Zero, "EDIT", null);
IntPtr htextbox2 =FindWindowEx(hwnd, htextbox, "EDIT", null);//填上次獲得的句柄,可以得到下一個的句柄。
這里只是先將第二個參數填為IntPtr.Zero,獲取第一個EDIT類型的文本框,然后第二次調用時,再將第二參數填為第一個文本框的句柄,那么執(zhí)行返回的就是下一個文本框的句柄了。因此htextbox2得到的就是第二文本框的句柄。
在可以自由獲得各種窗口及其上控件的句柄后,我們就可以向其發(fā)送各種消息進行鼠標和鍵盤的模擬了。比如:
SendMessage(htextbox,WM_SETTEXT, IntPtr.Zero, name);
這句是為文本框填寫相應的字符串name。
IntPtr hbutton = FindWindowEx(hwnd, IntPtr.Zero, "BUTTON", null);
SendMessage(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);
SendMessage(hbutton, WM_LBUTTONUP, IntPtr.Zero, null);
這三句是獲得了窗口的一個button,然后發(fā)送按下,彈起消息給它,模擬了點擊鼠標的動作。
SendMessage函數的第一個參數是窗口句柄,或者窗口中控件的句柄,第二個參數是消息的類型Flag,這些值是在API的一些頭文件中定義好的。你要是在C#中用,就自己去定義他們,比如
const int WM_SETTEXT = 0x000C;
const int WM_LBUTTONDOWN =0x0201;
const int WM_LBUTTONUP = 0x0202;
const intWM_CLOSE = 0x0010;
還有其他的類型Flag,可以參考上一篇Blog查詢,也可以去查MSDN。第三個參數和第四個參數都是消息的具體內容。一般我們用的是最后一個參數。第三個參數填為IntPtr.Zero。當然如果是鼠標的動作,那么最后一個參數就是null。
SendMessage(htextbox, WM_SETTEXT, IntPtr.Zero, name);//填寫文本框。
SendMessage(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);//鼠標按下按鈕。
OK,介紹完畢,收工。
P.s.
1. 可以用System.Diagnostics.Process p =System.Diagnostics.Process.Start啟動程序(exe)
2.接收消息的窗口為當前激活窗口,需要添加如下代碼:
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
并在發(fā)送消息前調用此方法。