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

系統匣程式開發實戰:鬧鐘 Alert 完整原始碼解析

     編輯:關於VC++

系統匣程式長駐工作列通知區域,核心在於 Shell_NotifyIcon() API(定義於 ShellAPI.h)。本文實作一個駐留系統匣的鬧鐘程式 Alert:設定時間到達時觸發、執行螢幕保護程式並播放音樂。

關鍵結構與 API

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;

Alert 核心程式碼實作

初始化:放入系統匣

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();
}

已知 Bug 與改進方向

程式結束後圖示殘留於系統匣,需移動滑鼠經過才消失。推測需在 OnCancel 中額外釋放圖示控制代碼 (DestroyIcon) 或確保 NIM_DELETE 完整執行。歡迎讀者提供解決方案。

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