=================================================================
//試圖取得可用的掃雷窗口,返回值表示是否成功。若成功,則全局變量
//MineWnd、MineDC、AreaHeight、AreaWidth都得到相應的填充。若失敗,則以上變量的值無意義。
function GetMineWindow: Boolean;
var
clIEntRect: TRect;
begin
result := false;
MineWnd := FindWindow(nil, MINE_WINDOW_TITLE); //檢查是否存在“掃雷”窗口,並且必須為當前窗口
if (MineWnd = 0) or (GetForegroundWindow <> MineWnd) then
Exit;
MineDC := GetDC(MineWnd); //取得“掃雷”窗口的設備上下文
if MineDC = 0 then
Exit;
GetClientRect(MineWnd, clIEntRect); //檢查“掃雷”窗口的內容是否全部顯示在屏幕上
with TCanvas.Create do
try
Handle := MineDC;
if (ClipRect.Left <> clIEntRect.Left) or
(ClipRect.Right <> clIEntRect.Right) or
(ClipRect.Top <> clIEntRect.Top) or
(ClipRect.Bottom <> clIEntRect.Bottom) then
Exit;
finally
Free;
end;
//從已獲得的clIEntRect中的數值,根據實測數據計算AreaWidth和AreaHeight的值。
AreaWidth := (clIEntRect.Right - LEFT_MARGIN - RIGHT_MARGIN) div CELL_WIDTH;
AreaHeight := (clIEntRect.Bottom - TOP_MARGIN - BOTTOM_MARGIN) div CELL_HEIGHT;
//檢查游戲是否在進行中,原理為判斷“重開始”按鈕的圖標上的
//某一像素是否是指定的值。該經驗由實測得到,只有游戲進行中,該像素才為該值。
if TColor(GetPixel(MineDC, AreaWidth*8 + 8, 30)) <> clBlack then
Exit;
result := true;
end;
=================================================================
理解這個函數的工作過程,有幾個要點:
WinAPI函數FindWindow:用來查找當前桌面上的某個窗口。第一個參數是指定該窗口的“窗口類”的名字,這個稍微高深了一點,只有研究過Windows SDK編程才會理解。當它為nil的時候,使用第二個參數,也就是窗口標題欄的字符串來查找。若找到這樣一個窗口,則返回值為其窗口句柄,否則為0。
WinAPI函數GetForegroundWindow:無參數,返回桌面上的當前窗口,也就是標題條加亮的窗口的句柄。
WinAPI函數GetDC:給定一個窗口句柄,返回它的設備上下文句柄。“設備上下文”實際上就是一個“畫布”,在Delphi中,被封裝成了TCanvas類。獲得了某個設備上下文句柄,就可以用一個TCanvas型的對象指向它(這個過程是,把句柄賦給TCanvas對象的Handle屬性),從而實現畫布的各種操作。
WinAPI函數GetClIEntRect:給定某個窗口句柄,取得它的客戶區矩形,這個矩形是一個TRect類型的變量。調用這個函數,要用一個TRect型的變量來接收結果,而不是用返回值。這個結果的Left和Top成員都必定是0,而Right和Bottom成員其實就是窗口客戶區的寬和高。
TCanvas類的屬性ClipRect:簡單的說,在此處,該TRect型屬性取得的是該畫布實際上被顯示在屏幕上的矩形部分。只有該畫布不被其它窗口遮擋,並且沒有移出桌面邊界的時候,這個矩形才完全等於等於窗口的客戶區矩形。這用來判斷掃雷窗口是否全部可見。
WinAPI函數GetPixel:給定一個設備上下文(畫布)句柄和X,Y坐標,取得一個像素的值。這個值是整型的,可以簡單的強制轉換為TColor類型。
上述庫函數,具體說明可以參考MSDN和Delphi自身的幫助文檔,可以得到最為權威、詳細、正確的說明。
不得不說一下GetMineWindow函數的最後幾行,它牽涉到了對“重開始”按鈕的hack。注意一下,可以發現那個簡單的臉譜總共有5種狀態:平時的笑臉,自身被按下時的笑臉,在雷區中按下鼠標時的緊張表情,觸雷時的衰臉和勝利時酷酷的表情~——顯然,只有在第一種情況時,掃雷外掛才應該動作,其它四種時則應該停止。我編了一個臨時程序,找到了一個像素位置,它只有在第一種情況下值為clBlack,其它情況都不是。它的坐標為(AreaWidth*8 + 8, 30),橫坐標是個隨方塊列數而變的變量,很好理解,因為無論窗口有多寬,該按鈕都是水平居中的。