自动化测试 (二) 连连看外挂
?
GUI自動化測試和做外掛的原理很相似,都是模擬用戶的鼠標和鍵盤操作, 給自己的程序寫自動化就是做測試,給別人的程序寫自動化就是外掛了。
本文使用的技術也同樣適用制作“對對碰”,"找茬" 之類游戲的外掛。
?
閱讀目錄
?
QQ連連看外掛實現原理
1. 先調用Win32 API獲取"連連看"游戲窗口的句柄,
2. 根據游戲窗口的句柄,然后獲取游戲方塊中的像素。
3. 用一個二維數組來保存每個方塊的像素
4. 用算法判定兩個一樣的方塊能否"消", 如果能"消"的話,就模擬鼠標去點擊這兩個方塊。 繼續"消" 下一組方塊。
?
GUI自動化測試的原理
當你點擊窗體中的一個button,?button會響應然后執行一些操作。 這個過程的本質是: 你在屏幕上點擊一個Button,??Windows系統根據你點擊的位置,知道你要點擊哪個Button,然后給這個Button發送鼠標點擊的消息。
自動化的原理是:? 找到控件的句柄,通過句柄給這個控件發送消息,比如“鍵盤輸入”消息或者“鼠標點擊”消息。
什么是句柄
所有的Windows控件本質上都是一個窗體(Window). 每個控件/窗體都有一個與之關聯的句柄(handle), 可以通過這個句柄來訪問,操縱和檢測這個控件/窗體
窗體句柄是由系統產生的一個值,你可以把它想象成與窗體關聯的一個ID,通過這個ID可以訪問相應的窗體。
在.NET中, 句柄的類型是System.IntPtr,? 有點類似Int型。
?
P/Invoke機制
P/invoke機制叫做"平臺調用"機制, 因為Win32API 函數是Windows操作系統的一部分,所以它是用傳統的C++程序寫的,而不是用C#托管代碼寫的。 所以我們需要一種機制,讓C#中可以調用Win32 API函數.
具體的解決方案是: 先為想要使用的Win32函數創建一個C#外覆函數,或者叫別名函數, 然后調用這個別名函數
?
實例:
在Win32 API中獲取窗體的句柄的函數是 FindWindow(),? 它的函數簽名用C++描述是這樣的
HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);在C#中,給這個Win32 函數創建別名函數
需要先引用命名空間: using System.Runtime.InteropServices;
[DllImport("user32.dll", EntryPoint = "FindWindow", CharSet = CharSet.Auto)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);DllImport: 指定要使用的函數所在的DLL文件。?
EntryPoint: Win32 API函數的名稱.
CharSet.Auto: 讓.NET框架來決定如何進行字符類型轉換
?
當為一個Win32函數編寫相應的C#方法別名的時候, 幾乎總要使用static 和extern, 因為大多數Win32函數都是靜態函數,而非實例函數。
注意:別名函數和Win32函數的名稱并不要求一致。 但是名稱一致可以保證代碼的可讀性.
?
獲取游戲窗體的句柄
我們用Win32 API 函數來獲取“連連看"游戲窗體的句柄
我們現在調用上節介紹的C#中的別名函數FindWindow(),就相當于調用了Win32 API 的FindWindow()函數
FindWindow函數接收2個參數,className 或者WindowName? 然后返回句柄.
?
Spy++是.NET中自帶工具,我們可以使用它來獲取窗體的名字。
具體用法是,啟動spy++, 點擊"Findow Window"圖標,彈出Findow Window 程序后, 用鼠標拖動“靶心”到你要測試的窗體上。
如下圖。 可以得到游戲窗體的名字叫"QQ游戲 - 連連看角色版"
這樣我們就能輕松獲取 游戲窗體的句柄
IntPtr wndPane = Win32API.FindWindow(null, "QQ游戲 - 連連看角色版");?
分析游戲窗口
通過屏幕標尺工具, 我們去測量游戲窗口。 (這個比較繁瑣,需要你多次去測量,多次調整后才能得到準確的數據).
可以發現 "游戲區域"? 距離游戲窗口 水平方向:15像素, 垂直方向:182像素
游戲中垂直方向有11個方塊, 水平方向有19個方塊
每個方塊 長:31像素, 寬:35像素? 如下圖
?
?
?
對游戲窗口中的所有方塊進行截圖
一個方塊有31*35=1085個像素, ? 事實上我們不需要獲取方塊中所有的像素點。 為了節省性能,我只需要獲取一個方塊中的幾個像素就可以了。
我們需要用到2個函數來實現獲取方塊的像素。?
?
[DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr hwnd);[DllImport("Gdi32.dll")] public static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);?
GetDC函數的作用是指定窗口的客戶區域或整個屏幕的顯示設備上下文環境的句柄, 它的輸入參數是窗口的句柄,? (上節中我們介紹過可以使用FindWindow函數來獲取窗口的句柄).? 返回的是DC句柄。? (注意這兩個句柄是不同的)
GetPixel根據GetDC獲取的DC句柄和X坐標,Y坐標來獲取像素點。
?
實例: 我們獲取游戲中 "第三排第四列" 的方塊的像素, 代碼如下:
IntPtr wndPane = Win32API.FindWindow(null, "QQ游戲 - 連連看角色版"); IntPtr hdc = Win32API.GetDC(wndPane); // X軸方向的像素要這么算15+31*3 // 因為游戲區域距離游戲窗口左邊15像素,每個方塊寬31像素, // Y軸方向的像素要這么算 182+35*4 // 因為游戲區域距離游戲窗口上方182,每個方塊高35像素 uint color = Win32API.GetPixel(hdc, 15+31*3 + offX, 182+35*4 + offY);?
根據游戲規則來寫算法
我們用一個二維數組來保存游戲中的所有方塊
private Block[,] blocks = new Block[11, 19];
Block對象代表一個方塊,如果方塊為空,那么Block包含的是背景色。 如果有方塊,那么Block對象中保存該方塊的9個像素點。
詳細請參考代碼中的Block對象。
?
然后分析游戲規則來寫算法來遍歷二維數組。
垂直方向,如果兩個一樣的方塊,處于同樣的Y軸上,中間沒有任何方塊可以消,? 如圖
?
水平方向,如果兩個一樣的方塊,處于同樣的X軸上,中間沒有任何方塊, 可以消,? 如圖
?
拐1個彎,? 如果兩個一樣的方塊, 其中一個的X軸和另一個Y成90度,并且中間沒有任何方塊, 可以消,? 如圖
?
拐2個彎,? 如圖
根據上面這些游戲規則,來設計算法, 具體算法請參考源代碼
?
?
模擬鼠標點擊
模擬鼠標點擊的的方法有很多,
其中的一個win32 API 方法為
?
[DllImport("user32.dll")]public static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);const int MOUSEEVENTF_MOVE = 0x0001; //移動鼠標public const int MOUSEEVENTF_LEFTDOWN = 0x0002; //模擬鼠標左鍵按下public const int MOUSEEVENTF_LEFTUP = 0x0004; //模擬鼠標左鍵抬起const int MOUSEEVENTF_RIGHTDOWN = 0x0008; //模擬鼠標右鍵按下const int MOUSEEVENTF_RIGHTUP = 0x0010; //模擬鼠標右鍵抬起const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; //模擬鼠標中鍵按下const int MOUSEEVENTF_MIDDLEUP = 0x0040; //模擬鼠標中鍵抬起const int MOUSEEVENTF_ABSOLUTE = 0x8000; //標示是否采用絕對坐標源代碼下載
請點擊這里下載源代碼,? 請用Visual Studio 2010打開。
總結
以上是生活随笔為你收集整理的自动化测试 (二) 连连看外挂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读《当下的力量》有感
- 下一篇: Geek