系統匣程式長駐工作列通知區域,核心在於 Shell_NotifyIcon() API(定義於 ShellAPI.h)。本文實作一個駐留系統匣的鬧鐘程式 Alert:設定時間到達時觸發、執行螢幕保護程式並播放音樂。
BOOL Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATA lpdata);
// dwMessage 動作:
// NIM_ADD 新增圖示
// NIM_DELETE 移除圖示
// NIM_MODIFY 修改圖示
// NIM_SETFOCUS 焦點歸還匣區 (Shell 5.0+)
// NIM_SETVERSION 指定 Shell 版本 (Shell 5.0+)
typedef struct _NOTIFYICONDATA {
DWORD cbSize; // 結構大小
HWND hWnd; // 接收訊息視窗
UINT uID; // 圖示 ID
UINT uFlags; // 有效欄位旗標 (NIF_ICON|NIF_MESSAGE|NIF_TIP...)
UINT uCallbackMessage; // 自訂回呼訊息 ID
HICON hIcon; // 圖示控制代碼
TCHAR szTip[64]; // 工具提示文字
// Shell 5.0+ 擴充欄位:
DWORD dwState; // 狀態 (NIS_HIDDEN|NIS_SHAREDICON)
DWORD dwStateMask;
TCHAR szInfo[256]; // 氣球提示文字
union { UINT uTimeout; UINT uVersion; };
TCHAR szInfoTitle[64]; // 氣球提示標題
DWORD dwInfoFlags; // 圖示類型 (NIIF_INFO|NIIF_WARNING...)
GUID guidItem; // 保留
} NOTIFYICONDATA;
BOOL CAlertDlg::OnInitDialog() {
SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE);
CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*)GetDlgItem(IDC_SPIN);
pSpin->SetRange(5, 150); m_Interval = 30;
CDialog::OnInitDialog();
SetTimer(1, 1000, NULL); // 每秒心跳
NOTIFYICONDATA nd = {0};
nd.cbSize = sizeof(nd);
nd.hWnd = m_hWnd;
nd.uID = IDI_ICON;
nd.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nd.uCallbackMessage = WM_NOTIFYICON; // 自訂 WM_USER+5
nd.hIcon = m_hIcon;
strcpy(nd.szTip, "我的鬧鐘");
Shell_NotifyIcon(NIM_ADD, &nd);
return TRUE;
}
void CAlertDlg::OnTimer(UINT nIDEvent) {
COleDateTime dt = COleDateTime::GetCurrentTime();
CString str; str.Format("現在時間 %02i:%02i:%02i", dt.GetHour(), dt.GetMinute(), dt.GetSecond());
GetDlgItem(IDC_CURTIME)->SetWindowText(str);
static int Count = 0;
if (nIDEvent == IDT_APPLY) Count = 0; // 重新設定間隔時歸零
Count++;
int Rest = m_Interval - Count/60;
if (Rest <= 0) { Count = 0; ShowMessage(); }
str.Format("離下次提醒還差 %i 分鐘", Rest);
GetDlgItem(IDC_RESTTIME)->SetWindowText(str);
CDialog::OnTimer(nIDEvent);
}
void CAlertDlg::ShowMessage() {
WinExec("C:\\INFOCD\\WINAMP\\WINAMP.EXE music.m3u", SW_HIDE); // 播放音樂
MessageBox("您該休息一會兒了......", "休息", MB_SYSTEMMODAL|MB_OK|MB_ICONEXCLAMATION|MB_ICONWARNING);
ShellExecute(m_hWnd, "open", "C:\\WINDOWS\\SYSTEM\\太空.scr", NULL, NULL, SW_SHOWNORMAL); // 執行螢幕保護
}
// 點擊匣區圖示顯示視窗
void CAlertDlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam) {
if (wParam == IDI_ICON && lParam == WM_LBUTTONDOWN) ShowWindow(SW_SHOWNORMAL);
}
// 最小化時隱藏而非縮到工作列;屏蔽最大化
void CAlertDlg::OnSysCommand(UINT nID, LPARAM lParam) {
if (nID == SC_MAXIMIZE) return;
if (nID == SC_MINIMIZE) ShowWindow(SW_HIDE);
else CWnd::OnSysCommand(nID, lParam);
}
// 使用者登出/關機時自動清理
BOOL CAlertDlg::OnQueryEndSession() {
OnCancel(); return TRUE;
}
void CAlertDlg::OnCancel() {
KillTimer(1);
NOTIFYICONDATA nd = {sizeof(nd), m_hWnd};
Shell_NotifyIcon(NIM_DELETE, &nd);
CDialog::OnCancel();
}
程式結束後圖示殘留於系統匣,需移動滑鼠經過才消失。推測需在 OnCancel 中額外釋放圖示控制代碼 (DestroyIcon) 或確保 NIM_DELETE 完整執行。歡迎讀者提供解決方案。