首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

防止 Explorer 重啟導致系統托盤圖示消失的 C++Builder 完整解決方案

     編輯:關於C++

問題背景

許多常駐程式在系統托盤 (通知區域) 顯示圖示提供快速存取。當 Explorer.exe 異常終止並自動重啟時,托盤區域會重建,但應用程式若未感知此事件,原有圖示將永久消失,導致使用者無法互動、甚至誤以為程式已關閉。本文提供完整的 C++Builder (VCL) 解決方案,攔截 TaskbarCreated 訊息自動重建圖示。

核心機制:TaskbarCreated 訊息

Windows 在重建工作列時,會向所有頂層視窗廣播一個已註冊的自訂訊息:

UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");

應用程式只需在視窗程序中攔截此訊息,呼叫重新加入托盤的邏輯即可。

完整 VCL 實作程式碼

1. 表單標頭檔 (Unit1.h)

#ifndef Unit1H
#define Unit1H

#include 
#include 
#include 
#include 
#include 
#include   // NOTIFYICONDATA, Shell_NotifyIcon

class TForm1 : public TForm {
__published:
    TButton *btnTest;
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall btnTestClick(TObject *Sender);
private:
    static UINT WM_TASKBARCREATED;      // 工作列重建訊息 ID
    static UINT WM_ICONNOTIFY;          // 托盤圖示回呼訊息 ID
    NOTIFYICONDATA m_nid;               // 托盤圖示資料結構
    void AddTrayIcon();                 // 加入托盤
    void RemoveTrayIcon();              // 移除托盤
protected:
    virtual void __fastcall WndProc(TMessage &Message);  // 重載視窗程序
public:
    __fastcall TForm1(TComponent* Owner);
};

extern PACKAGE TForm1 *Form1;
#endif

2. 表單實作檔 (Unit1.cpp)


#include 
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
UINT TForm1::WM_TASKBARCREATED = 0;
UINT TForm1::WM_ICONNOTIFY = 0;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender) {
    // 註冊系統級訊息
    WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
    WM_ICONNOTIFY     = RegisterWindowMessage(L"TrayIconNotify");  // 自訂回呼訊息

    AddTrayIcon();
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) {
    RemoveTrayIcon();
}

//---------------------------------------------------------------------------
void __fastcall TForm1::WndProc(TMessage &Message) {
    // 1. 處理托盤圖示回呼 (滑鼠左鍵雙擊、右鍵選單等)
    if (Message.Msg == WM_ICONNOTIFY) {
        if (Message.LParam == WM_LBUTTONDBLCLK) {
            // 雙擊托盤圖示:顯示/隱藏主視窗
            Visible ? Hide() : (Show(), BringToFront());
        }
        else if (Message.LParam == WM_RBUTTONUP) {
            // 右鍵:顯示快速選單 (需自行建立 TPopupMenu)
            // PopupMenu1->Popup(Mouse->CursorPos.X, Mouse->CursorPos.Y);
        }
        return;
    }

    // 2. 關鍵:攔截工作列重建訊息
    if (Message.Msg == WM_TASKBARCREATED) {
        AddTrayIcon();  // 重新註冊圖示
        return;
    }

    // 3. 其他訊息交給基底類別
    TForm::WndProc(Message);
}

//---------------------------------------------------------------------------
void TForm1::AddTrayIcon() {
    ZeroMemory(&m_nid, sizeof(m_nid));
    m_nid.cbSize           = sizeof(NOTIFYICONDATA);
    m_nid.hWnd             = Handle;
    m_nid.uID              = 1;  // 圖示識別碼 (單一圖示固定為 1)
    m_nid.uFlags           = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
    m_nid.uCallbackMessage = WM_ICONNOTIFY;
    m_nid.hIcon            = Application->Icon->Handle;  // 使用應用程式圖示
    lstrcpyn(m_nid.szTip, Application->Title.c_str(), sizeof(m_nid.szTip)/sizeof(TCHAR));

    Shell_NotifyIcon(NIM_ADD, &m_nid);
}

//---------------------------------------------------------------------------
void TForm1::RemoveTrayIcon() {
    m_nid.cbSize = sizeof(NOTIFYICONDATA);
    m_nid.hWnd   = Handle;
    m_nid.uID    = 1;
    Shell_NotifyIcon(NIM_DELETE, &m_nid);
}

//---------------------------------------------------------------------------
void __fastcall TForm1::btnTestClick(TObject *Sender) {
    ShowMessage(L"主視窗顯示正常,托盤圖示運作中。");
}

關鍵細節解析

  1. RegisterWindowMessage:系統級唯一訊息 ID,跨進程有效,避免衝突。
  2. NOTIFYICONDATA.cbSize:必須正確設定結構大小,Vista+ 建議使用 NOTIFYICONDATA_V4_SIZE 或 sizeof(NOTIFYICONDATA) (配合 _WIN32_IE >= 0x0600)。
  3. uCallbackMessage:自訂訊息 (WM_ICONNOTIFY) 用於接收滑鼠事件,lParam 包含 WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, WM_RBUTTONUP 等。
  4. NIF_SHOWTIP:Vista+ 強制顯示工具提示,避免系統設定隱藏。
  5. 圖示資源:Application->Icon->Handle 取得應用程式主圖示;亦可用 LoadImage 載入獨立 .ico 檔。

驗證測試方法

  1. 編譯執行程式,確認托盤區出現圖示。
  2. 開啟「工作管理員」→ 詳細資料 → 找到 explorer.exe → 右鍵「結束工作」。
  3. 觀察螢幕閃爍 (Explorer 重啟),托盤區圖示應**自動重現**,無需人工干預。
  4. 點擊/右鍵圖示驗證回呼功能正常。

進階擴充建議

  • 多圖示支援:以 uID 區分多個托盤圖示,std::map 管理。
  • 高 DPI 圖示:準備 16×16、32×32、48×48 多解析度 .ico,或使用 LoadImage 配合 LR_DEFAULTSIZE 自動縮放。
  • Shell_NotifyIcon 版本:Vista+ 呼叫 Shell_NotifyIcon(NIM_SETVERSION, &nid) 設定 nid.uVersion = NOTIFYICON_VERSION_4 以啟用豐富工具提示、氣球通知。
  • 單一實例守護:配合 CreateMutex 確保僅一實例運行,避免重複註冊托盤圖示。

結語

托盤圖示消失是經典的 Explorer 重啟問題,根源在於應用程式未處理 TaskbarCreated 訊息。本文提供的 VCL 方案僅需 3 步驟:註冊訊息 → 重載 WndProc 攔截 → 重建圖示,即可徹底解決。此模式同樣適用於 Delphi、C++ (Win32 SDK)、C# (WndProc 覆寫) 等所有 Windows 桌面開發平台。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved