CPropertyPage 對應 5 個標籤頁,動態建立/顯示/隱藏。CStatic 子類化為 CMyPic。WM_LBUTTONDOWN:SetCapture()、載入準星游標 (IDC_CURSOR1)、切換無靶圖標 (IDI_ICON2)。WM_LBUTTONUP:ReleaseCapture()、恢復有靶圖標 (IDI_ICON1)、啟動主對話框定時器 (ID=1, 600ms)。OnTimer)
void CSpyXXDlg::OnTimer(UINT nIDEvent)
{
if (nIDEvent == 1) {
POINT pt; ::GetCursorPos(&pt);
HWND hWnd = ::WindowFromPoint(pt); // 取得游標下窗口句柄
g_hWnd = hWnd; // 全域變數共享給各屬性頁
// 繪製閃爍矩形框 (R2_NOTXORPEN 模式)
HWND hDesk = ::GetDesktopWindow();
HDC hDC = ::GetWindowDC(hDesk);
int oldROP = SetROP2(hDC, R2_NOTXORPEN);
RECT rc; ::GetWindowRect(hWnd, &rc);
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(0,0,0));
HGDIOBJ oldPen = SelectObject(hDC, hPen);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
Sleep(100); // 閃爍間隔
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); // 再畫一次抹除
SelectObject(hDC, oldPen); DeleteObject(hPen);
SetROP2(hDC, oldROP);
::ReleaseDC(hDesk, hDC);
}
CDialog::OnTimer(nIDEvent);
}
CPage0):基礎屬性
// 句柄 (16/10 進制切換)
pPage0->m_editHWND.SetWindowText(Display((int)g_hWnd));
// 類名
char szClass[256]; ::GetClassName(g_hWnd, szClass, 256);
pPage0->m_editCLASS.SetWindowText(szClass);
// 標題
char szTitle[256]; ::GetWindowText(g_hWnd, szTitle, 256);
pPage0->m_editTITLE.SetWindowText(szTitle);
// 窗口 ID
pPage0->m_editWNDID.SetWindowText(Display(GetWindowLong(g_hWnd, GWL_ID)));
// 進程 ID + 路徑
DWORD dwPID; GetWindowThreadProcessId(g_hWnd, &dwPID);
pPage0->m_editPID.SetWindowText(Display(dwPID));
pPage0->m_editPATH.SetWindowText(GetProcPath(dwPID)); // ToolHelp32 快照獲取路徑
// 窗口矩形
RECT rc; GetWindowRect(g_hWnd, &rc);
CStr.Format(_T("(%d,%d)-(%d,%d) %dx%d"), rc.left,rc.top,rc.right,rc.bottom, rc.right-rc.left, rc.bottom-rc.top);
pPage0->m_editRECT.SetWindowText(CStr);
CPage1):位元組解碼GetWindowLong(hWnd, GWL_STYLE) / GWL_EXSTYLE 取得樣式位元組。WS_* / WS_EX_* 常數,(style & WS_BORDER) ? AddString : Skip。g_nHex 控制 Display() 格式化。CPage2):GetClassLong 同樣列舉 CS_* 樣式。CPage3):導航窗口樹關鍵 API 組合:
| 關係 | API | 參數 |
|---|---|---|
| 上一個同級 | GetNextWindow | GW_HWNDPREV |
| 下一個同級 | GetNextWindow | GW_HWNDNEXT |
| 父窗口 | GetParent | - |
| 第一個子窗口 | GetWindow | GW_CHILD |
| 擁有者窗口 | GetWindow | GW_OWNER |
標題獲取用 SendMessage(hWnd, WM_GETTEXT, 255, (LPARAM)buf) 替代 GetWindowText,可讀取系統/隱藏窗口標題。
CPage4):CCheckListBox 互動控制Owner Draw: Fixed + Has Strings。CListBox 變數強制轉型 CCheckListBox (繼承關係允許)。LBN_SELCHANGE 判斷 GetCheck(index) 執行對應 API:
// 範例:置頂切換
if (GetCheck(2)) SetWindowPos(g_hWnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
else SetWindowPos(g_hWnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
// 互斥邏輯:最大化/最小化/還原 三選一
CMyPic 截獲按鈕非標準訊息;CCheckListBox 變數轉型法避免新建類。g_hWnd, g_hPage0..4, g_nHex 簡化跨模組數據流 (單執行緒 UI 安全)。GetWindowDC(GetDesktopWindow()) + R2_NOTXORPEN 實現無痕閃爍框。CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, PID) 兼容 9x/NT 獲取進程路徑。本專案在 WinXP + VC6.0 調試通過。原始碼包含完整資源與類別實現,是學習 Windows 窗口管理、訊息機制、Spy++ 內部原理的極佳教具。