標籤:style blog http java color 使用 os 檔案
GUI自動化測試和做外掛的原理很相似,都是類比使用者的滑鼠和鍵盤操作, 給自己的程式寫自動化就是做測試,給別人的程式寫自動化就是外掛了。
本文使用的技術也同樣適用製作“對對碰”,"找茬" 之類遊戲的外掛。
閱讀目錄
- QQ連連看外掛實現原理
- GUI自動化測試的原理
- 什麼是控制代碼
- P/Invoke機制
- 擷取遊戲表單的控制代碼
- 分析遊戲視窗
- 對遊戲視窗進行
- 根據遊戲規則來寫演算法
- 類比滑鼠點擊
- 原始碼下載
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#Managed 程式碼寫的。 所以我們需要一種機制,讓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開啟。
附: 自動化測試 系列教程, (連載中, 敬請期待)
自動化測試 (一) 12306火車票網站自動登入工具
自動化測試 (二) 連連看外掛
自動化測試 (三) Web自動化測試原理
自動化測試 (四) 自動卸載軟體
自動化測試 (五) 讀寫64位作業系統的註冊表