系統托盤圖示是 Windows 桌面應用常見功能,讓程式常駐背景執行並提供快速互動入口。本文透過完整的 Win32 API 範例,詳細解析托盤圖示的建立、訊息處理、右鍵選單、雙擊退出與任務列重建容錯等核心機制。
NOTIFYICONDATA nid; // 托盤圖示資訊結構 HMENU hmenu; // 右鍵彈出選單控制代碼 UINT WM_TASKBARCREATED; // 任務列重建自訂訊息
nid.cbSize = sizeof(nid);
nid.hWnd = hwnd;
nid.uID = 0;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER; // 自訂回呼訊息
nid.hIcon = LoadIcon(NULL, IDI_APPLICATION);
lstrcpy(nid.szTip, szAppName);
Shell_NotifyIcon(NIM_ADD, &nid); // 加入托盤區
/* 建立右鍵選單 */
hmenu = CreatePopupMenu();
AppendMenu(hmenu, MF_STRING, IDR_PAUSE, TEXT("暫停服務"));
AppendMenu(hmenu, MF_STRING, IDR_START, TEXT("恢復服務"));
case WM_USER:
if (lParam == WM_LBUTTONDOWN) {
MessageBox(hwnd, TEXT("雙擊托盤可退出!"), szAppName, MB_OK);
}
if (lParam == WM_LBUTTONDBLCLK) { // 雙擊退出
SendMessage(hwnd, WM_CLOSE, wParam, lParam);
}
if (lParam == WM_RBUTTONDOWN) { // 右鍵顯示選單
GetCursorPos(&pt);
SetForegroundWindow(hwnd); // 解決選單焦點問題
EnableMenuItem(hmenu, IDR_PAUSE, MF_GRAYED); // 動態啟用/停用項目
int cmd = TrackPopupMenu(hmenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL);
if (cmd == IDR_PAUSE) MessageBox(hwnd, TEXT("111"), szAppName, MB_OK);
if (cmd == IDR_START) MessageBox(hwnd, TEXT("222"), szAppName, MB_OK);
}
break;
Shell_NotifyIcon(NIM_DELETE, &nid); PostQuitMessage(0);
WM_TASKBARCREATED = RegisterWindowMessage(TEXT("TaskbarCreated"));
/* 在預設訊息處理中攔截 */
if (message == WM_TASKBARCREATED)
SendMessage(hwnd, WM_CREATE, wParam, lParam); // 重新註冊圖示
原理:Explorer.exe 崩潰重啟後會重建任務列,並向所有頂層視窗廣播 TaskbarCreated 訊息。捕捉此訊息並重新呼叫 NIM_ADD 即可恢復圖示。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR szCmdLine, int iCmdShow) {
/* 單一實例檢查 */
HWND handle = FindWindow(NULL, szWndName);
if (handle) { MessageBox(...); return 0; }
/* 註冊視窗類別 */
WNDCLASS wc = { CS_HREDRAW|CS_VREDRAW, WndProc, 0, 0, hInstance,
LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
(HBRUSH)GetStockObject(WHITE_BRUSH), NULL, szAppName };
RegisterClass(&wc);
/* 建立隱藏視窗 (WS_EX_TOOLWINDOW 不顯示在工作列) */
hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, szAppName, szWndName,
WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
/* 訊息迴圈 */
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
| 項目 | 說明 |
|---|---|
| 圖示資源 | 實際專案應用 LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON)) 載入自訂 .ico |
| Unicode 相容 | 全程使用 TEXT() / _T() 宏與 TCHAR 類型 |
| 選單狀態 | 動態 EnableMenuItem/CheckMenuItem 反映程式狀態 |
| 資源釋放 | 結束前必須 DestroyMenu(hmenu)、Shell_NotifyIcon(NIM_DELETE) |
| 高 DPI | Vista+ 應用 NIF_HIGHRES 或提供多尺寸圖示 |