托盤區基本概念
- 位於工作列通知區域 (系統托盤),提供常駐程式快速存取入口。
- 核心 API:
Shell_NotifyIcon (shellapi.h)。
- 關鍵結構:
NOTIFYICONDATA (NID)。
NOTIFYICONDATA 關鍵欄位
typedef struct _NOTIFYICONDATA {
DWORD cbSize; // sizeof(NOTIFYICONDATA)
HWND hWnd; // 接收回呼訊息的視窗
UINT uID; // 圖示識別碼 (同一視窗多圖示時區分)
UINT uFlags; // 指出哪些欄位有效
// NIF_MESSAGE: uCallbackMessage 有效
// NIF_ICON: hIcon 有效
// NIF_TIP: szTip 有效
// NIF_STATE: dwState/dwStateMask 有效 (Win2000+)
// NIF_INFO: szInfo/szInfoTitle/dwInfoFlags 有效 (氣球提示)
UINT uCallbackMessage; // 自訂訊息 (如 WM_USER+1),滑鼠事件透過此訊息通知
HICON hIcon; // 托盤圖示句柄
TCHAR szTip[128]; // 滑鼠懸停提示文字
// 以下為 NIF_INFO / NIF_STATE 擴充欄位...
} NOTIFYICONDATA;
核心操作流程
- 加入圖示:
Shell_NotifyIcon(NIM_ADD, &nid)
- 修改圖示/提示:
Shell_NotifyIcon(NIM_MODIFY, &nid) (更新 hIcon/szTip)。
- 刪除圖示:
Shell_NotifyIcon(NIM_DELETE, &nid) (程式結束時必須呼叫)。
完整實作範例 (主視窗隱藏,雙擊圖示顯示/隱藏)
// Unit1.h
class TForm1 : public TForm {
private:
UINT FIconMsg; // 自訂訊息 ID
NOTIFYICONDATA FNid;
void __fastcall AddTrayIcon();
void __fastcall RemoveTrayIcon();
protected:
virtual void __fastcall WndProc(TMessage &Message); // 重寫視窗程序
void __fastcall TrayIconMsg(TMessage &Msg); // 處理托盤訊息
public:
__fastcall TForm1(TComponent* Owner);
__fastcall ~TForm1();
};
// Unit1.cpp
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
FIconMsg = RegisterWindowMessage("MyApp_TrayIconMsg"); // 唯一訊息 ID
AddTrayIcon();
ShowWindow(Handle, SW_HIDE); // 啟動時隱藏主視窗
}
__fastcall TForm1::~TForm1() {
RemoveTrayIcon();
}
void __fastcall TForm1::AddTrayIcon() {
ZeroMemory(&FNid, sizeof(FNid));
FNid.cbSize = sizeof(FNid);
FNid.hWnd = Handle;
FNid.uID = 1;
FNid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
FNid.uCallbackMessage = FIconMsg;
FNid.hIcon = Application->Icon->Handle; // 使用應用程式圖示
StrLCopy(FNid.szTip, "我的托盤程式", sizeof(FNid.szTip)/sizeof(TCHAR));
Shell_NotifyIcon(NIM_ADD, &FNid);
}
void __fastcall TForm1::RemoveTrayIcon() {
FNid.cbSize = sizeof(FNid);
FNid.hWnd = Handle;
FNid.uID = 1;
Shell_NotifyIcon(NIM_DELETE, &FNid);
}
void __fastcall TForm1::WndProc(TMessage &Message) {
if (Message.Msg == FIconMsg) {
TrayIconMsg(Message); // 處理托盤滑鼠事件
return;
}
TForm::WndProc(Message); // 繼承鏈
}
void __fastcall TForm1::TrayIconMsg(TMessage &Msg) {
switch (Msg.LParam) { // lParam 為滑鼠事件
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
// 可在這裡追蹤滑鼠位置顯示右鍵選單
break;
case WM_LBUTTONDBLCLK: // 雙擊左鍵
Visible = !Visible; // 切換主視窗顯示/隱藏
if (Visible) SetForegroundWindow(Handle);
break;
case WM_RBUTTONUP: {
// 顯示右鍵選單
POINT pt; GetCursorPos(&pt);
SetForegroundWindow(Handle); // 修正選單焦點問題
PopupMenu1->Popup(pt.x, pt.y);
break;
}
}
}
進階功能
- 氣球提示 (Win2000+):設定
NIF_INFO,填寫 szInfo, szInfoTitle, dwInfoFlags (NIIF_INFO/WARNING/ERROR/USER/NONE),呼叫 NIM_MODIFY。
- 動態圖示:定時器輪換
hIcon (如網速、CPU 使用率圖示)。
- 高 DPI:提供 16x16, 32x32, 48x48 多尺寸圖示資源,或使用
LoadImage 載入 PNG 並轉 HICON (需 Comctl32.dll 6.0+)。
- 多圖示:不同
uID 區分,分別 NIM_ADD。
常見坑點
- 未在
FormDestroy 呼叫 NIM_DELETE → 托盤留下「殭屍圖示」,需重啟 Explorer 消除。
uCallbackMessage 用 WM_USER+1 易衝突,**務必用 RegisterWindowMessage 取得唯一 ID**。
- 右鍵選單彈出後不消失:呼叫
SetForegroundWindow(Handle) 先讓視窗獲取焦點。
- 視窗隱藏時
Application->Icon 可能無效,建議直接載入 .ico 資源 LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON))。