Windows 任務列 是一個標準應用程式,由多個子視窗組成。本文示範如何在 C++Builder 中定位「開始」按鈕視窗,並利用 GDI 將滑鼠游標周圍的桌面畫面即時複製繪製至按鈕上,形成「透視」視覺效果。此技術屬於進階視窗操作範疇,僅供教學研究使用,實際部署需考慮系統相容性與使用者體驗。
使用 Spy++ 可觀察到以下關鍵視窗結構:
Shell_TrayWndButton,為 Shell_TrayWnd 的子視窗FindWindow("Shell_TrayWnd", NULL) → 取得任務列句柄FindWindowEx(hTray, 0, "Button", NULL) → 取得開始按鈕句柄GetDC(hStartBtn) → 取得按鈕裝置內容 (DC)GetDC(GetDesktopWindow()) → 取得桌面 DCGetCursorPos(&pt) → 取得滑鼠座標StretchBlt(hBtnDC, 0,0,60,25, hDeskDC, pt.x-30,pt.y-12,60,25, SRCCOPY) → 將滑鼠周圍 60x25 區域拉伸繪製至按鈕ReleaseDC → 釋放 DC
void __fastcall TForm1::FormCreate(TObject *Sender) {
Application->MessageBox(
"C++Builder 繪圖於開始按鈕示範程式",
"說明", MB_OK | MB_DEFBUTTON1);
// 1. 取得任務列視窗
HWND hTray = FindWindow("Shell_TrayWnd", NULL);
// 2. 取得開始按鈕 (Shell_TrayWnd 的第一個 Button 子視窗)
HWND hStart = FindWindowEx(hTray, 0, "Button", NULL);
// 3. 取得並保存 DC (需在 FormDestroy 釋放)
hdcButton = GetDC(hStart);
hdcDesktop = GetDC(GetDesktopWindow());
// 4. 設定高頻定時器 (1ms ≈ 1000 FPS,實際受 GDI 限制)
Timer1->Enabled = false;
Timer1->Interval = 1;
BitBtn1->Tag = 0; // 0=停止, 1=執行
BitBtn1->Caption = "開始繪圖";
}
void __fastcall TForm1::BitBtn1Click(TObject *Sender) {
if (BitBtn1->Tag == 0) {
Timer1->Enabled = true;
BitBtn1->Caption = "結束繪圖";
BitBtn1->Tag = 1;
} else {
Close(); // 關閉表單將觸發 FormDestroy 釋放資源
}
}
void __fastcall TForm1::Timer1Timer(TObject *Sender) {
TPoint pt;
GetCursorPos(&pt); // 取得螢幕座標
// 將桌面 (滑鼠中心 60x25) 複製到開始按鈕 (60x25)
StretchBlt(
hdcButton, // 目標 DC
0, 0, 60, 25, // 目標矩形
hdcDesktop, // 來源 DC
pt.x - 30, pt.y - 12, // 來源矩形左上角 (置中滑鼠)
60, 25, // 來源寬高
SRCCOPY // 光柵操作:直接複製
);
}
void __fastcall TForm1::FormDestroy(TObject *Sender) {
if (hdcButton) ReleaseDC(FindWindowEx(FindWindow("Shell_TrayWnd",NULL),0,"Button",NULL), hdcButton);
if (hdcDesktop) ReleaseDC(GetDesktopWindow(), hdcDesktop);
}
GetCursorPos 回傳**螢幕座標**GetDC(GetDesktopWindow()) 的座標原點也是螢幕左上角GetDC(hStartBtn) 的座標原點是**按鈕客戶區左上角**StretchBlt 同時指定目標與來源矩形,自動處理縮放與座標轉換| 作業系統 | 開始按鈕特徵 | 備註 |
|---|---|---|
| Windows 95/98/NT/2000 | 單一 Button 子視窗 | 本範例適用 |
| Windows XP | 佈景主題化,可能非標準 Button | 需啟用視覺樣式或改用主題 API |
| Vista/7/8/10/11 | 任務列重寫 (DWM 合成),開始按鈕為獨立視窗 | FindWindowEx 可能失效,需枚舉子視窗配合類別名/樣式過濾 |