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

系統托盤程式設計完全指南 (1):從 Shell_NotifyIcon 到 CTrayIcon 封裝類別

     編輯:關於VC++

系統托盤(通知區域)自 Windows 95 以來成為長時間執行應用程式(防毒、即時通訊、媒體播放器、輸入法)的標準 UI 模式。MFC 未提供現成類別,開發者需直接呼叫 Shell_NotifyIcon API。本文系列將從零建立可重用的 CTrayIcon 類別,涵蓋圖示管理、ToolTip、右鍵選單、雙擊行為、Explorer 重啟自動恢復、氣球提示等完整功能。

核心 API:Shell_NotifyIcon


BOOL Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATA pnid);

dwMessage 操作類型:

值用途
NIM_ADD新增圖示到通知區
NIM_MODIFY修改現有圖示(圖示、ToolTip 等)
NIM_DELETE移除圖示
NIM_SETFOCUS將焦點還給通知區(如 ESC 取消選單時)
NIM_SETVERSION指定 Shell 版本行為 (IE4/IE5)

NOTIFYICONDATA 關鍵欄位:


typedef struct _NOTIFYICONDATA {
    DWORD cbSize;           // sizeof(NOTIFYICONDATA) 必填
    HWND  hWnd;             // 接收回呼訊息的視窗
    UINT  uID;              // 圖示識別碼 (多圖示時區分)
    UINT  uFlags;           // 有效欄位旗標組合
    UINT  uCallbackMessage; // 自訂回呼訊息 ID
    HICON hIcon;            // 圖示控制代碼
    CHAR  szTip[64];        // ToolTip 文字
} NOTIFYICONDATA;

uFlags 組合:NIF_MESSAGE (啟用回呼)、NIF_ICON (圖示有效)、NIF_TIP (ToolTip 有效)。

事件通知機制

設定 uCallbackMessage = WM_MY_TRAY_NOTIFY 並包含 NIF_MESSAGE。使用者在圖示上滑鼠動作(移動、單擊、雙擊、右鍵)時,Windows 向 hWnd 送出 WM_MY_TRAY_NOTIFY:

  • wParam = uID (圖示 ID)
  • lParam = 滑鼠訊息 (WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONUP, WM_LBUTTONDBLCLK 等)

CTrayIcon 封裝設計目標

隱藏 NOTIFYICONDATA、旗標組合、訊息代碼等繁瑣細節,提供直觀介面:

  • SetIcon(UINT nResourceID) — 以資源 ID 設定圖示,自動讀取同 ID 字串資源作 ToolTip
  • SetIcon(HICON) / SetStandardIcon(IDI_WINLOGO) — 直接用控制代碼或系統圖示
  • SetIcon(0) — 移除圖示 (內部轉 NIM_DELETE)
  • SetNotificationWnd(CWnd*, UINT msg) — 指定接收通知的視窗與訊息 ID
  • OnTrayNotification(WPARAM, LPARAM) — 預設處理右鍵彈選單、雙擊執行首項命令

使用範例:最小化至托盤的對話框應用

1. 主框架類別宣告


class CMainFrame : public CFrameWnd {
protected:
    CTrayIcon m_trayIcon;           // 托盤圖示實例
    // ...
};

2. 建構函式初始化圖示 ID


CMainFrame::CMainFrame() : m_trayIcon(IDR_TRAYICON) { }

IDR_TRAYICON 同時作為:圖示資源 ID、ToolTip 字串資源 ID、右鍵選單資源 ID。

3. OnCreate 中安裝圖示與註冊通知


#define WM_MY_TRAY_NOTIFICATION (WM_USER + 0)

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
    
    m_trayIcon.SetNotificationWnd(this, WM_MY_TRAY_NOTIFICATION);
    m_trayIcon.SetIcon(IDI_MYICON); // 圖示 + ToolTip
    return 0;
}

4. 訊息映射處理通知


BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_MESSAGE(WM_MY_TRAY_NOTIFICATION, OnTrayNotification)
END_MESSAGE_MAP()

LRESULT CMainFrame::OnTrayNotification(WPARAM wp, LPARAM lp)
{
    // 可自行處理特定滑鼠事件 (如記錄 Log)
    return m_trayIcon.OnTrayNotification(wp, lp); // 委派預設行為
}

5. 右鍵選單資源 (IDR_TRAYICON)


IDR_TRAYICON MENU DISCARDABLE
BEGIN
    POPUP "托盤(&T)"
    BEGIN
        MENUITEM "開啟(&O)",           ID_APP_OPEN
        MENUITEM "關於 TrayTest(&A)...", ID_APP_ABOUT
        MENUITEM SEPARATOR
        MENUITEM "結束 TrayTest(&S)",   ID_APP_SUSPEND
    END
END

CTrayIcon 自動:

  1. 右鍵點擊 → TrackPopupMenu 顯示選單(首項設為預設黑體)
  2. 雙擊左鍵 → 執行首項命令 (ID_APP_OPEN)
  3. 選單項命令透過 WM_COMMAND 走標準 MFC 路由

關鍵技術細節:TrackPopupMenu 焦點問題

若直接呼叫 TrackPopupMenu,使用者按 ESC 或點選單外區域,選單不會自動消失。解決方案 (MSDN Q135788):


::SetForegroundWindow(m_nid.hWnd); // 關鍵:先設定前景視窗
::TrackPopupMenu(pSubMenu->m_hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, 
                 pt.x, pt.y, 0, m_nid.hWnd, NULL);

此為 CTrayIcon 內建處理,使用者無需關心。

範例程式:TrayTest1

  • 啟動顯示對話框(圖一),點「確定」最小化至托盤。
  • 雙擊托盤圖示 → 彈出訊息記錄視窗(圖二),記錄所有滑鼠事件。
  • 滑鼠懸停圖示 → 顯示 ToolTip(圖三)。
  • 右鍵圖示 → 彈出選單(圖四),選「結束」才真正退出程式。
  • 關閉主視窗/選單「檔案→結束」 → 僅隱藏,不退出(覆寫 OnClose)。

設計原則警語

托盤圖示並非所有應用程式都需要。濫用會造成通知區擁擠(圖五「噩夢版」),使用者體驗極差。請遵循:

  • ✅ 長時間背景執行、需偶爾通知使用者
  • ✅ 提供「隱藏托盤圖示」選項
  • ❌ 短期工具、安裝精靈、遊戲等無需常駐

系列規劃

  1. ✅ 基礎 API、CTrayIcon 封裝、選單/ToolTip/雙擊行為(本文)
  2. ⏭️ TaskbarCreated 自動重建圖示 (Explorer 重啟恢復)
  3. ⏭️ 氣球提示 Balloon Tips (Win2000/XP)

完整原始碼含 CTrayIcon.h/cpp、TrayTest1 專案,歡迎下載參考。

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