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

VC++ 實現最小化到系統托盤 (System Tray) 完整範例:仿 QQ 後台駐留

     編輯:VC 教學

讓對話框程式最小化時隱藏主視窗、在任務列通知區域 (系統托盤) 顯示圖標,右鍵彈出選單可「還原」或「退出」,是背景服務類應用 (如 IM、下載器、監控) 的標準交互。本文提供基於 NOTIFYICONDATA 與 Shell_NotifyIcon 的完整實現代碼。

核心結構與 API

typedef struct _NOTIFYICONDATA {
    DWORD cbSize;           // sizeof(NOTIFYICONDATA)
    HWND  hWnd;             // 接收通知訊息的視窗
    UINT  uID;              // 圖標識別碼 (自訂)
    UINT  uFlags;           // NIF_MESSAGE | NIF_ICON | NIF_TIP
    UINT  uCallbackMessage; // 自訂訊息 (如 WM_MY_TRAY = WM_USER+1)
    HICON hIcon;            // 圖標句柄
    TCHAR szTip[128];       // 滑鼠懸停提示文字
} NOTIFYICONDATA;

BOOL Shell_NotifyIcon(DWORD dwMessage, NOTIFYICONDATA* lpData);
// dwMessage: NIM_ADD (添加), NIM_MODIFY (修改), NIM_DELETE (刪除)

完整實現步驟 (對話框類 CMyDlg)

1. 定義自訂訊息與映射

// .h
#define WM_MY_TRAY_MSG (WM_USER + 1)
afx_msg LRESULT OnTrayNotify(WPARAM wParam, LPARAM lParam);
// .cpp 訊息映射
ON_MESSAGE(WM_MY_TRAY_MSG, OnTrayNotify)

2. OnInitDialog 建立托盤圖標

BOOL CMyDlg::OnInitDialog() {
    CDialog::OnInitDialog();

    NOTIFYICONDATA nid = {0};
    nid.cbSize = sizeof(nid);
    nid.hWnd = m_hWnd;
    nid.uID = IDR_MAINFRAME; // 使用應用程式圖標 ID
    nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    nid.uCallbackMessage = WM_MY_TRAY_MSG;
    nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    _tcscpy_s(nid.szTip, _T("我的程式 - 雙擊還原"));

    Shell_NotifyIcon(NIM_ADD, &nid);
    return TRUE;
}

3. 處理托盤訊息 (左鍵雙擊還原、右鍵彈選單)

LRESULT CMyDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam) {
    if (wParam != IDR_MAINFRAME) return 0;

    switch (lParam) {
    case WM_LBUTTONDBLCLK: // 雙擊左鍵還原
        ShowWindow(SW_RESTORE);
        SetForegroundWindow(); // 置頂
        break;

    case WM_RBUTTONUP: { // 右鍵彈出選單
        CMenu menu;
        menu.LoadMenu(IDR_TRAY_MENU); // 資源建立選單 IDR_TRAY_MENU
        CMenu* pPopup = menu.GetSubMenu(0);
        if (pPopup) {
            CPoint pt; GetCursorPos(&pt);
            // TPM_LEFTALIGN | TPM_RIGHTBUTTON 讓右鍵也能選擇
            pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
        }
        break;
    }
    }
    return 0;
}

4. 選單命令處理 (資源選單項 ID_EXIT, ID_RESTORE)

void CMyDlg::OnTrayRestore() { ShowWindow(SW_RESTORE); SetForegroundWindow(); }
void CMyDlg::OnTrayExit() {
    // 退出前必須移除托盤圖標,否則殘留「幽靈圖標」
    NOTIFYICONDATA nid = {0};
    nid.cbSize = sizeof(nid);
    nid.hWnd = m_hWnd;
    nid.uID = IDR_MAINFRAME;
    Shell_NotifyIcon(NIM_DELETE, &nid);

    EndDialog(IDOK); // 或 PostMessage(WM_CLOSE)
}

5. 攔截最小化/關閉系統命令

void CMyDlg::OnSysCommand(UINT nID, LPARAM lParam) {
    if ((nID & 0xFFF0) == SC_MINIMIZE) {
        ShowWindow(SW_HIDE); // 隱藏而非最小化到任務列
        return;
    }
    if ((nID & 0xFFF0) == SC_CLOSE) {
        // 視需求決定:真正退出 或 也隱藏到托盤
        // 這裡示範真正退出 (調用 OnTrayExit 邏輯)
        OnTrayExit();
        return;
    }
    CDialog::OnSysCommand(nID, lParam);
}

6. 視窗銷毀時清理 (雙重保險)

void CMyDlg::OnDestroy() {
    NOTIFYICONDATA nid = {0};
    nid.cbSize = sizeof(nid);
    nid.hWnd = m_hWnd;
    nid.uID = IDR_MAINFRAME;
    Shell_NotifyIcon(NIM_DELETE, &nid);
    CDialog::OnDestroy();
}

資源準備

  • 圖標:專案圖標 IDR_MAINFRAME (16x16, 32x32 皆含)。
  • 選單:資源建立選單 IDR_TRAY_MENU,含「還原」(ID_TRAY_RESTORE)、分隔線、「退出」(ID_TRAY_EXIT),並用 ClassWizard 映射命令處理函數。

避坑指南

  1. Unicode 字串複製: 使用 _tcscpy_s / StringCchCopy,避免 strcpy 緩衝區溢出。
  2. 幽靈圖標: 程式異常退出未執行 NIM_DELETE 會留下圖標,滑鼠劃過才消失。OnDestroy 刪除是最後防線。
  3. Vista+ 高 DPI: 圖標建議包含 16/32/48/256 像素多種尺寸,或使用 LoadImage 載入 PNG 圖標 (IMAGE_ICON, LR_DEFAULTSIZE)。
  4. 多實例: uID 需區分 (如 IDR_MAINFRAME + GetCurrentProcessId()),否則第二個實例 NIM_ADD 會失敗。

此模式已廣泛應用於即時通訊、備份同步、系統監控等長期駐留型應用。

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