在 Windows 環境下,許多應用程式啟動時會在系統托盤(通知區域)顯示一個小圖標。正常情況下,程式結束時會自動移除該圖標,但若應用程式異常終止(如強制關閉、崩潰),圖標往往會殘留在托盤區,直到使用者將滑鼠游標移過它們才會自動消失。這不僅影響視覺美觀,也可能誤導使用者以為程式仍在執行。
針對這個問題,我們可以透過模擬滑鼠游標逐一掃過托盤區每個圖標的位置,觸發系統重繪機制,從而自動清除那些已失效的圖標。下面提供一個完整的 C++ 實作範例(適用於 C++ Builder / Delphi 環境),核心邏輯是取得托盤視窗句柄與範圍、計算圖標網格座標、依序移動滑鼠游標,最後還原游標位置並強制重繪托盤視窗。
void __fastcall RemoveDeadIcons()
{
HWND hTrayWindow;
RECT rctTrayIcon;
int nIconWidth, nIconHeight;
TPoint CursorPos;
int nRow, nCol;
// 取得托盤通知視窗句柄:Shell_TrayWnd -> TrayNotifyWnd
hTrayWindow = FindWindowEx(FindWindow("Shell_TrayWnd", NULL), 0, "TrayNotifyWnd", NULL);
if (!GetWindowRect(hTrayWindow, &rctTrayIcon)) return;
// 取得系統小圖標尺寸
nIconWidth = GetSystemMetrics(SM_CXSMICON);
nIconHeight = GetSystemMetrics(SM_CYSMICON);
// 儲存目前滑鼠位置
GetCursorPos(&CursorPos);
// 依網格掃描托盤區每個圖標位置
for (nRow = 0; nRow < (rctTrayIcon.bottom - rctTrayIcon.top) / nIconHeight; nRow++) {
for (nCol = 0; nCol < (rctTrayIcon.right - rctTrayIcon.left) / nIconWidth; nCol++) {
SetCursorPos(
rctTrayIcon.left + nCol * nIconWidth + 5,
rctTrayIcon.top + nRow * nIconHeight + 5
);
Sleep(0); // 讓出時間片,確保系統處理訊息
}
}
// 還原滑鼠位置
SetCursorPos(CursorPos.x, CursorPos.y);
// 強制重繪托盤視窗(修正多行托盤區的已知問題)
RedrawWindow(hTrayWindow, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
}
FindWindow 找到 Shell_TrayWnd(工作列主視窗),再用 FindWindowEx 定位其子視窗 TrayNotifyWnd(通知區域)。GetSystemMetrics(SM_CXSMICON/SM_CYSMICON) 取得小圖標寬高,配合托盤視窗矩形區域,推算出每個圖標的中心座標。SetCursorPos 將游標移至每個推算座標,系統偵測到游標進入圖標區域,會觸發 WM_MOUSEMOVE 等訊息,進而檢查圖標對應的程序是否仍存在,若不存在則自動移除。RedrawWindow 強制更新托盤視窗,確保殘留圖徹底消失。此函式可整合至系統維護工具、啟動器或看門狗程式中,定期或在特定事件(如偵測到程序異常退出)後呼叫。需注意:
ITrayNotify 介面。BlockInput 暫時封鎖輸入。Sleep(0) 讓出時間片,避免佔用過多 CPU,實際延遲極小。透過這個簡單卻有效的技巧,即可保持系統托盤區域乾淨,提升使用者體驗。