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

Visual C++ 系統匣盤程式開發完整指南:隱藏主視窗、圖示互動、右鍵選單與動態切換

     編輯:C++ 入門

系統匣盤程式架構核心

  • **無主視窗顯示**:`ShowWindow(SW_HIDE)` 或 `PreCreateWindow` 設定 `WS_POPUP | WS_EX_TOOLWINDOW`。
  • **任務列通知區圖示**:`Shell_NotifyIcon(NIM_ADD, &nid)`,`NOTIFYICONDATA` 指定 `hWnd`、`uCallbackMessage`、`hIcon`、`szTip`。
  • **自訂回呼訊息**:`WM_USER + N` (如 `WM_TRAYNOTIFY`),主視窗訊息映射處理 `WM_LBUTTONDOWN`、`WM_RBUTTONUP`、`WM_MOUSEMOVE`、`WM_LBUTTONDBLCLK` 等 `lParam` 事件。
  • **右鍵彈出選單**:`TrackPopupMenu` + `CMenu`,座標轉螢幕座標 `ClientToScreen`。
  • **動態切換圖示**:`Shell_NotifyIcon(NIM_MODIFY, &nid)` 更新 `hIcon`。
  • **任務列重建恢復**:註冊 `TaskbarCreated` 訊息,收到時重發 `NIM_ADD`。

完整最小可行程式碼 (MFC)


// TrayApp.h
class CTrayApp : public CWinApp {
    CMainFrame* m_pFrame = nullptr;
    NOTIFYICONDATA m_nid = { sizeof(NOTIFYICONDATA) };
    UINT m_uTaskbarRestart = 0;
    static const UINT WM_TRAYNOTIFY = WM_USER + 1;
public:
    virtual BOOL InitInstance() override;
    void SetupTrayIcon();
    void RemoveTrayIcon();
    afx_msg LRESULT OnTaskbarRestart(WPARAM, LPARAM);
    DECLARE_MESSAGE_MAP()
};

// TrayApp.cpp
BEGIN_MESSAGE_MAP(CTrayApp, CWinApp)
    ON_REGISTERED_MESSAGE(WM_TRAYNOTIFY, OnTrayNotify) // 若用 ON_MESSAGE 需靜態
    ON_REGISTERED_MESSAGE(RegisterWindowMessage(_T("TaskbarCreated")), OnTaskbarRestart)
END_MESSAGE_MAP()

BOOL CTrayApp::InitInstance() {
    m_pFrame = new CMainFrame;
    if (!m_pFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;
    m_pFrame->ShowWindow(SW_HIDE); // 關鍵:隱藏主視窗
    m_pFrame->UpdateWindow();
    m_uTaskbarRestart = RegisterWindowMessage(_T("TaskbarCreated"));
    SetupTrayIcon();
    return TRUE;
}

void CTrayApp::SetupTrayIcon() {
    m_nid.hWnd = m_pFrame->m_hWnd;
    m_nid.uID = IDR_MAINFRAME;
    m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    m_nid.uCallbackMessage = WM_TRAYNOTIFY;
    m_nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    _tcscpy_s(m_nid.szTip, _T("My Tray Application"));
    Shell_NotifyIcon(NIM_ADD, &m_nid);
}

void CTrayApp::RemoveTrayIcon() {
    Shell_NotifyIcon(NIM_DELETE, &m_nid);
}

LRESULT CTrayApp::OnTaskbarRestart(WPARAM, LPARAM) {
    SetupTrayIcon(); // 任務列重建後重新加入
    return 0;
}

// MainFrm.cpp 訊息處理
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_MESSAGE(WM_TRAYNOTIFY, OnTrayNotify)
END_MESSAGE_MAP()

LRESULT CMainFrame::OnTrayNotify(WPARAM wParam, LPARAM lParam) {
    if (wParam != IDR_MAINFRAME) return 0;
    switch (lParam) {
        case WM_LBUTTONDBLCLK:
            ShowWindow(SW_SHOW); // 雙擊顯示主視窗
            SetForegroundWindow();
            break;
        case WM_RBUTTONUP: {
            CMenu menu; menu.LoadMenu(IDR_TRAY_MENU);
            CMenu* pPopup = menu.GetSubMenu(0);
            CPoint pt; GetCursorPos(&pt);
            SetForegroundWindow(); // 關鍵:否則選單不會在點擊外消失
            pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
            break;
        }
        case WM_MOUSEMOVE:
            // 可動態更新提示文字
            _tcscpy_s(m_nid.szTip, _T("Mouse over tray icon"));
            Shell_NotifyIcon(NIM_MODIFY, &m_nid);
            break;
    }
    return 0;
}

資源定義要點

  • **圖示資源** `IDR_MAINFRAME`:包含 16×16、32×32、48×48 多解析度,支援高 DPI。
  • **選單資源** `IDR_TRAY_MENU`:頂層選單含一個子選單,項目如「顯示主視窗」、「設定」、「退出」。
  • **字串表**:提示文字、選單項目文字集中管理,便於多語系。

高 DPI 與現代 Windows 相容性

  1. **Manifest 宣告 DPI 感知**:
    ` True/PM/PerMonitorV2 `
  2. **圖示載入用 `LoadImage` + `LR_DEFAULTSIZE`** 讓系統自動選擇最適解析度。
  3. **Win10/11 通知區行為**:圖示預設隱藏於溢出區,用戶需手動釘選;程式不可強制顯示。

常見坑與解法

問題原因解法
右鍵選單彈出後不隨點擊外部消失未呼叫 `SetForegroundWindow()``TrackPopupMenu` 前呼叫 `SetForegroundWindow()`
雙擊顯示視窗後,單擊任務列圖示無法再次隱藏視窗狀態邏輯混亂維護 `bool m_bVisible` 狀態,切換時同步 `ShowWindow` 與選單項目勾選狀態
任務列重啟 (Explorer.exe 重啟) 後圖示消失未處理 `TaskbarCreated``ON_REGISTERED_MESSAGE(RegisterWindowMessage(_T("TaskbarCreated")), OnTaskbarRestart)` 重 `NIM_ADD`
圖示模糊/尺寸錯誤單一解析度圖示 / 未宣告 DPI 感知多解析度 `.ico` + Manifest `dpiAware`
記憶體洩漏:退出時未 `NIM_DELETE`未在 `ExitInstance`/`OnDestroy` 清理覆寫 `ExitInstance` 呼叫 `RemoveTrayIcon()`

現代化替代方案

  • **WinUI 3 / Windows App SDK**:原生 `TrayIcon` API (Windows 11 SDK 1.4+),XAML 介面、高 DPI、打包部署 (MSIX) 一體化。
  • **Qt / wxWidgets / Electron / Tauri**:跨平台系統匣盤支援 (`QSystemTrayIcon` / `wxTaskBarIcon` / `tray` npm 套件)。
  • **Rust `tray-icon` / Go `systray` / Python `pystray`**:輕量、原生、部署簡單。

結語

系統匣盤程式看似簡單,**訊息迴路、圖示生命週期、高 DPI、任務列重建、選單焦點** 細節決定體驗品質。掌握 MFC 經典實作後,**建議新專案直接採用現代原生/跨平台框架**,避免維護傳統 MFC 技術債。

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