前一篇建立了基本托盤功能,但存在嚴重缺陷:Windows Explorer 異常終止後重啟,托盤圖示消失不見,使用者必須重啟應用程式才能恢復。本文解決此問題,核心在於攔截 TaskbarCreated 廣播訊息並重新註冊圖示。
Explorer 管理工作列(含通知區域)。當 Explorer 進程終止(使用者在工作管理員「結束程序」、或系統更新重啟),所有托盤圖示隨之消失。Explorer 重啟後,會向所有最上層視窗廣播已註冊訊息 TaskbarCreated。應用程式若處理此訊息並再次呼叫 Shell_NotifyIcon(NIM_ADD, ...),圖示即可自動恢復。
// 全域/靜態變數保存訊息 ID
const UINT WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_REGISTERED_MESSAGE(WM_TASKBARCREATED, OnTaskBarCreated)
END_MESSAGE_MAP()
LRESULT CMainFrame::OnTaskBarCreated(WPARAM, LPARAM)
{
VERIFY(InstallIcons()); // 重新安裝圖示
return 0;
}
InstallIcons() 需獨立實作(而非直接在 OnCreate 內寫死),因啟動時與重建時均需呼叫。
為免每個專案重複撰寫攔截碼,CTrayIcon 內建處理:
CSubclassWnd(視窗程序子類化工具類)掛鉤最上層父視窗程序。TaskbarCreated 時自動呼叫虛擬函式 OnTaskBarCreate(),預設實作為重新安裝圖示。CMainFrame::OnCreate 中:
m_trayIcon.SetNotificationWnd(this, WM_MY_TRAY_NOTIFICATION);
m_trayIcon.SetIcon(IDI_MYICON); // 內部自動啟動子類化攔截
CSubclassWnd 透過 SetWindowLong(GWL_WNDPROC) 替換視窗程序,先於 MFC 訊息映射處理訊息。流程:
CTrayIcon::SetNotificationWnd 呼叫 GetTopLevelParent() 取得框架視窗。CSubclassWnd Hook,攔截所有訊息。WM_TASKBARCREATED → 呼叫 CTrayIcon::OnTaskBarCreate() → 重建圖示。此技巧極為通用,亦可用於攔截 WM_QUERYNEWPALETTE、WM_SYSCOLORCHANGE 等僅送往最上層視窗的系統廣播。
TrayTest2.exe,確認托盤有圖示。explorer)。同樣適用於 WinNT/2000/XP(需安裝 IE4+)。
CTrayIcon::CTrayHook::WindowProc — 子類化視窗程序,攔截 TaskbarCreated。CTrayIcon::OnTaskBarCreate() — 虛擬函式,可覆寫自訂重建邏輯(如動態圖示、狀態恢復)。CTrayIcon::SetNotificationWnd — 整合通知視窗設定與子類化安裝。CTrayIcon 封裝、右鍵選單、雙擊預設命令掌握此機制,托盤程式即具備生產級穩健性。完整原始碼請下載附件。