客戶需臨時修改某數據庫程式顯示的數據以應付檢查,要求不更改真實數據庫。初嘗試「螢幕取詞」:攔截顯示數據、乘以基數、回寫螢幕。後客戶追加要求「列印修改後數據」,導致方案失效,最終改修改資料庫。但螢幕攔截技術本身有獨立價值,故整理分享。
核心技術棧:API 攔截、訊息鉤子、枚舉子視窗、系統熱鍵註冊。DLL 實作前三者,EXE 實作熱鍵與自訂訊息/系統托盤圖示。
繁體中文版,代碼精簡。核心步驟:
GetProcAddress 取被攔截函數與替換函數位址。__asm 組裝跳轉機器碼。VirtualProtect 修改頁面屬性。三大注意點:
比喻:潛入老闆辦公室 → 替換獎金單 → 還原現場 → 撤離。
核心 API:SetWindowsHookEx / UnhookWindowsHookEx。建議初學者:
SetWindowsHookEx 四參數,重點關注第 1 (鉤子類型) 與第 4 (執行緒 ID/DLL 實例)。少用 WH_CALLWNDPROC 除非必要。通常 WH_KEYBOARD/WH_MOUSE/WH_GETMESSAGE 即夠用。
兩種主流方法:
EnumChildWindows 回呼函數
// 呼叫
EnumChildWindows(g_hWndTag, EnumChildWindowsProc, 0);
// 回呼
BOOL CALLBACK EnumChildWindowsProc(HWND hWnd, LPARAM lParam) {
char buff[256] = {'\0'};
if (::GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) {
::GetWindowText(hWnd, buff, 256);
// 過濾特定按鈕/視窗
if (strstr(buff, "列印") || strstr(buff, "Excel") || strstr(buff, "導出")) {
// EnableWindow(hWnd, FALSE); // 僅禁用
DestroyWindow(hWnd); // 徹底銷毀
}
}
return TRUE; // 繼續枚舉
}
GetWindow 迴圈 (百試不爽)
HWND hWndChild = ::GetWindow(g_hWnd, GW_CHILD);
while (NULL != hWndChild) {
// styleXP.CreateClassXP(hWndChild); // 如換膚用途
hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT);
}
警示:迴圈條件嚴謹,防止無限迴圈。換膚程式常用此法子類化視窗重繪,但工作量極大、易崩潰,作者嘗試重繪按鈕後放棄。
三步曲:
#define ID_A 1501
#define ID_B 1502
#define ID_C 1503
#define ID_D 1504
#define ID_E 1505
void CTestDlg::RegSysHotkey() {
HWND hWnd = this->m_hWnd;
RegisterHotKey(hWnd, ID_A, MOD_ALT, 65); // Alt+A
RegisterHotKey(hWnd, ID_B, MOD_ALT, 66); // Alt+B
RegisterHotKey(hWnd, ID_C, MOD_ALT, 67); // Alt+C
RegisterHotKey(hWnd, ID_D, MOD_ALT, 68); // Alt+D
RegisterHotKey(hWnd, ID_E, MOD_ALT, 69); // Alt+E
}
PreTranslateMessage 攔截 WM_HOTKEY
BOOL CTestDlg::PreTranslateMessage(MSG* pMsg) {
switch (pMsg->message) {
case WM_HOTKEY:
switch (pMsg->wParam) {
case ID_A: OnAppShow(); break; // 顯示視窗
case ID_B: HideMe(); break; // 徹底隱藏
case ID_C: ... break; // 其他功能
}
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
代碼中已實作,此處不贅述,請參考原始碼註解。
readme、代碼註解,或至 VCKBASE 論壇發問。
本文整合四大核心 Windows 進階技術,雖源於「作弊」需求,但技術本身中性,廣泛應用於自動化測試、螢幕閱讀器、視窗管理工具、熱鍵增強軟體等。掌握底層機制、注重邊界條件與錯誤處理,方能寫出穩健程式。