托盘程序“ 我的小闹钟 ”
——第一个MFC程序
前言:要临近期末考试了,却突然对MFC发生了兴趣,在网上找了个用MFC编写的小闹钟的程序(作者Abbey,源自www.vckbase.com),仅仅只看了它的一些关于托盘程序的介绍便迫不及待的动手编制了我的第一个MFC程序。小小一个小闹钟却用了我一个晚上加一个上午的时间,感慨良久:或许看书是学习程序设计的一种方法,但从书上获得知识却远不及从实践获得的知识真实和印象深刻。
正题:
功能:我的应用程序界面如下图所示(当然是有点单调拉):能够自动获取系统时间,在编辑框设定的时间间隔后弹出消息框并播放一段音乐提示,点击“隐藏闹钟”按钮将该界面隐藏到系统托盘区。
实现:MFC框架没有提供任何现成的类应用于系统托盘UI,那么如何将表示应用程序的图标添加到任务栏中呢?方法很简单,只用到一个API函数,它就是Shell_NotifyIcon。这个函数本身也相当容易理解和使用。看看它的原型就知道了:
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA pnid
);
第一个参数dwMessage类型为DWORD,表示要进行的动作,它可以是下面的值之一:
NIM_ADD: 添加一个图标到任务栏。
NIM_MODIFY: 修改状态栏区域的图标。
NIM_DELETE: 删除状态栏区域的图标。
NIM_SETFOCUS: 将焦点返回到任务栏通知区域。当完成用户界面操作时,任务栏图标必须用此消息。例如,如果任务栏图标正显示上下文菜单,但用户按下"ESCAPE"键取消操作,这时就必须用此消息将焦点返回到任务栏通知区域。
NIM_SETVERSION:指示任务栏按照相应的动态库版本工作。
第二个参数pnid是NOTIFYICONDATA结构的地址,其内容视dwMessage的值而定。这个结构在SHELLAPI.H文件中定义如下:
typedef struct _NOTIFYICONDATA {
DWORD cbSize; // 结构大小(sizeof struct),必须设置
HWND hWnd; // 发送通知消息的窗口句柄
UINT uID; // 图标ID ( 由回调函数的WPARAM 指定)
UINT uFlags;
UINT uCallbackMessage; // 消息被发送到此窗口过程
HICON hIcon; // 图标句柄
CHAR szTip[64]; // 提示文本
} NOTIFYICONDATA;
uFlags的值:
#define NIF_MESSAGE 0x1 // 表示uCallbackMessage 有效
#define NIF_ICON 0x2 // 表示hIcon 有效
#define NIF_TIP 0x4 // 表示szTip 有效
下面就具体实现我的小闹钟啦!
先创建一个基于WIN32的对话框应用项目Alert;
然后设计如图的界面图,静态文本框1显示时间,ID 是IDC_CURTIME,静态文本框2提示距离设定时间的时间间隔,ID是IDC_RESTTIME,为编辑框设置变量m_nInterval;
再就是具体的编码阶段啦:
(1)在CAlert.h中添加头文件:
#define WM_NOTIFYICON WM_USER+5 //自定义消息的ID
#define IDI_ICON 0x0005 //图标ID
#define IDT_APPLY WM_USER+6 //使用的定时器Timer的自定义消息ID
(2)为CAlertDlg添加成员变量m_Interval,m_spin和成员函数,如下:
protected:
int m_Interval; //定时间隔,以分钟为单位
HICON m_hIcon; //图标句柄
void ShowMessage(void); //自定义的定时触发时运行的函数
// Generated message map functions
afx_msg void OnApply();//“应用”按钮的点击消息处理函数
virtual void OnCancel(); //"退出"按钮的点击消息处理函数
afx_msg void OnTimer(UINT nIDEvent); //定时器消息处理函数
afx_msg void OnClickHide(); //"隐藏"按钮的点击消息处理函数
//自定义WM_NOTIFYICON消息的处理函数
afx_msg void OnNotifyIcon(WPARAM wParam, LPARAM lParam);
afx_msg BOOL OnQueryEndSession(); //Windows关闭消息处理函数
启用类向导为以上消息处理函数设置消息映射关系,MFC自动形成以下消息映射:
BEGIN_MESSAGE_MAP(CAlertDlg, CDialog)
//{{AFX_MSG_MAP(CAlertDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnApply)
ON_BN_CLICKED(IDC_BUTTON3, OnCancel)
ON_BN_CLICKED(IDC_BUTTON2, OnClickHide)
ON_MESSAGE(WM_NOTIFYICON,OnNotifyIcon) //用户自定义消息,用手工添加
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
(3)下面是类的具体实现:
BOOL CAlertDlg::OnInitDialog()
{
CDialog::OnInitDialog();
…………..//MFC自动生成的代码省略啦
// TODO: Add extra initialization here
CWnd::SetWindowText("我的小闹钟!");
// 初始化时间间隔
CSpinButtonCtrl * pSpin;
pSpin = (CSpinButtonCtrl *) GetDlgItem(IDC_SPIN1);
pSpin->SetRange(0,150)//时间间隔的范围
pSpin->SetBuddy(GetDlgItem(IDC_EDIT1));//将旋转按钮和编辑框关联起来
m_Interval = 150;//缺省的时间间隔
// 设置定时器
SetTimer(1, 1000, NULL);
// 将图标放入系统托盘
NOTIFYICONDATA nd;
nd.cbSize = sizeof (NOTIFYICONDATA);
nd.hWnd = m_hWnd;
nd.uID = IDI_ICON;
nd.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;
nd.uCallbackMessage= WM_NOTIFYICON;
nd.hIcon = m_hIcon;
strcpy(nd.szTip, "我的闹钟");
Shell_NotifyIcon(NIM_ADD, &nd);
return TRUE; // return TRUE unless you set the focus to a control
}
void CAlertDlg::OnCancel()
{
// 释放定时器
KillTimer(1);
// 将图标从系统托盘中删除
NOTIFYICONDATA nd;
nd.cbSize = sizeof (NOTIFYICONDATA);
nd.hWnd = m_hWnd;
nd.uID = IDI_ICON;//图标的ID一定要指明,否则将产生一个小BUG
Shell_NotifyIcon(NIM_DELETE, &nd);
CDialog::OnCancel();
}
void CAlertDlg::ShowMessage()
{
//调用操作系统自带的播放器播放音乐清单
::WinExec("C://Program Files//Windows Media Player//wmplayer.exe C://WINDOWS//Media//flourish.mid",SW_HIDE);
MessageBox("您该休息一会儿了......",
"休息", MB_SYSTEMMODAL|MB_OK|MB_ICONEXCLAMATION|MB_ICONWARNING);
m_Interval=150;//恢复缺省的定时间隔
}
afx_msg void CAlertDlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
// 响应在托盘图标上的单击
//wParam中是响应消息的图标ID,lParam中则是Windows的消息
if ((wParam == IDI_ICON)&&(lParam == WM_LBUTTONDOWN))
ShowWindow(SW_SHOWNORMAL);
}
void CAlertDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
// 屏蔽最大化(MFC Bug?),将最小化重定向至隐藏窗口
if (nID == SC_MAXIMIZE)
return;
if (nID == SC_MINIMIZE)
ShowWindow(SW_HIDE);
else
CWnd::OnSysCommand(nID, lParam);
}
BOOL CAlertDlg::OnQueryEndSession()
{
// 在用户退出Windows时自动退出应用程序
CAlertDlg::OnCancel();
return TRUE;
}
void CAlertDlg::OnTimer(UINT nIDEvent)
{
static CString strTemp;
static int Count = 0;
COleDateTime dtTime;
// 刷新显示的时间
dtTime = COleDateTime::GetCurrentTime();
strTemp.Format("现在时间 %02i:%02i:%02i ",
dtTime.GetHour(),
dtTime.GetMinute(),
dtTime.GetSecond());
GetDlgItem(IDC_CURTIME)->SetWindowText(strTemp);
// 刷新剩余时间值
if(nIDEvent==IDT_APPLY) {m_Interval=m_nInterval;Count = 0;} //若用户重新定义了时间间隔,则重新开始计时
Count++;
int RestTime;
RestTime = m_Interval - Count/60;
if (RestTime <= 0) //定时时间到
{
Count = 0;
ShowMessage();
}
strTemp.Format("离下次提醒还差 %i 分钟%i", RestTime,Count);
GetDlgItem(IDC_RESETTIME)->SetWindowText(strTemp);
CDialog::OnTimer(nIDEvent);
}
void CAlertDlg::OnApply()
{
// TODO: Add your control notification handler code here
// 重置时间间隔
UpdateData();
OnTimer(IDT_APPLY);
}
void CAlertDlg::OnClickHide()
{
// TODO: Add your control notification handler code here
OnSysCommand(SC_MINIMIZE, 0x0000);
return;
}
最后编译,运行,不用说拉。
好啦,这就是创作我得小闹钟的全部过程。通过这个小程序总算弄懂啦MFC的消息映射(特别是自定义消息)啊,知道怎么样创建一个小托盘啦,有些收获吧。