数据结构: 有关的数据由NOTIFYICONDATA结构描述: typedef struct _NOTIFYICONDATA { DWORD cbSize; //结构的大小,必须设置 HWND hWnd; //接受回调消息的窗口的句柄 UINT uID; //应用程序定义的图标标志 UINT uFlags; //标志,可以是NIF_ICON、NIF_MESSAGE、NIF_TIP或其组合 UINT uCallbackMessage;//应用程序定义的回调消息标志 HICON hIcon; //图标句柄 char szTip[64]; //提示字串 } NOTIFYICONDATA, *PNOTIFYICONDATA; 函数说明 由Shell_NotifyIcon()函数向系统发送添加、删除、更改图标的消息。 WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(DWORD dwMessage,PNOTIFYICONDATA pnid); DwMessage为所发送消息的标志: NIM_ADD 添加图标到任务栏通知区; NIM_DELETE 删除任务栏通知区的图标; NIM_MODIFY 更改任务栏通知区的图标、回调消息标志、回调窗口句柄或提示字串; pnid为NOTIFYICONDATA结构的指针。 回调信息的获得及处理 如果一个任务栏图标有应用程序定义的回调消息,那么当这个图标有鼠标操作时,系统将给hWnd所标志的窗口发送下列的消息: messageID = uCallbackMessage wParam = uID lParam = mouse event(例如WM_LBUTTONDOWN) 通过这种方式,系统通知应用程序用户对图标的操作。如果一个应用程序生成了两个以上的图标,那么你可以根据wParam来判断是哪个图标返回的鼠标操作。通常,标准的Win95任务栏图标有以下鼠标操作响应: 当鼠标停留在图标上时,系统应显示提示信息tooltip; 当使用鼠标右键单击图标时,应用程序应显示快捷菜单; 当使用鼠标左键双击图标时,应用程序应执行快捷菜单的缺省菜单项。 在Microsoft Windows环境中,0x8000到0xBFFF的消息是保留的,应用程序可以定义自定义消息。 关于消息处理的详细内容,请参考下一部分。 源码及实现 在本文中关于任务栏图标的类叫做CTrayIcon,这个类由CCmdTarget(或CObject)类派生,它有如下的成员变量和成员函数: // TrayIcon.h // CTrayIcon command target class CTrayIcon : public CCmdTarget { public: NOTIFYICONDATA m_nid;//NOTIFYICONDATA结构,你的图标要用的啊 BOOL m_IconExist;//标志,看看图标是不是已经存在了 CWnd* m_NotificationWnd;//接受回调消息的窗口,有它就不必经常AfxGetMainWnd了 public: CWnd* GetNotificationWnd() const;//得到m_NotificationWnd BOOL SetNotificationWnd(CWnd* pNotifyWnd);//设置(更改)m_NotificationWnd CTrayIcon();//构造函数 virtual ~CTrayIcon();//析构函数 BOOL CreateIcon(CWnd* pNotifyWnd, UINT uID, HICON hIcon, LPSTR lpszTip, UINT CallBackMessage);//在任务栏上生成图标 BOOL DeleteIcon();//删除任务栏上的图标 virtual LRESULT OnNotify(WPARAM WParam, LPARAM LParam);//消息响应函数 BOOL SetTipText(UINT nID);//设置(更改)提示字串 BOOL SetTipText(LPCTSTR lpszTip);//设置(更改)提示字串 BOOL ChangeIcon(HICON hIcon);//更改图标 BOOL ChangeIcon(UINT nID);//更改图标 BOOL ChangeIcon(LPCTSTR lpszIconName);//更改图标 BOOL ChangeStandardIcon(LPCTSTR lpszIconName);//更改为标准图标 ...... }; 下面是成员函数的定义: // TrayIcon.cpp // CTrayIcon CTrayIcon::CTrayIcon() {//初始化参数 m_IconExist = FALSE; m_NotificationWnd = NULL; memset(&m_nid, 0, sizeof(m_nid)); m_nid.cbSize = sizeof(m_nid);//这个参数不会改变 } CTrayIcon::~CTrayIcon() { if (m_IconExist) DeleteIcon();//删除图标 } BOOL CTrayIcon::CreateIcon(CWnd* pNotifyWnd, UINT uID, HICON hIcon, LPSTR lpszTip, UINT CallBackMessage) { //确定接受回调消息的窗口是有效的 ASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->GetSafeHwnd())); ASSERT(CallBackMessage >= WM_USER);//确定回调消息不发生冲突 ASSERT(_tcslen(lpszTip) <= 64);//提示字串不能超过64个字符 m_NotificationWnd = pNotifyWnd;//获得m_NotificationWnd //设置NOTIFYICONDATA结构 m_nid.hWnd = pNotifyWnd->GetSafeHwnd(); m_nid.uID = uID; m_nid.hIcon = hIcon; m_nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; m_nid.uCallbackMessage = CallBackMessage; //设置NOTIFYICONDATA结构的提示字串 if (lpszTip) lstrcpyn(m_nid.szTip, lpszTip, sizeof(m_nid.szTip)); else m_nid.szTip[0] = '\0'; //显示图标 m_IconExist = Shell_NotifyIcon(NIM_ADD, &m_nid); return m_IconExist; } BOOL CTrayIcon::DeleteIcon() {//删除图标 if (!m_IconExist) return FALSE; m_IconExist = FALSE; return Shell_NotifyIcon(NIM_DELETE, &m_nid); } LRESULT CTrayIcon::OnNotify(WPARAM WParam, LPARAM LParam) {//处理图标返回的消息 if (WParam != m_nid.uID)//如果不是该图标的消息则迅速返回 return 0L; //准备快捷菜单 CMenu menu; if (!menu.LoadMenu(IDR_POPUP))//你必须确定资源中有ID为IDR_POPUP的菜单 return 0; CMenu* pSubMenu = menu.GetSubMenu(0);//获得IDR_POPUP的子菜单 if (!pSubMenu) return 0; if (LParam == WM_RBUTTONUP) {//右键单击弹出快捷菜单 //设置第一个菜单项为缺省 ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE); CPoint pos; GetCursorPos(&pos); //显示并跟踪菜单 m_NotificationWnd->SetForegroundWindow(); pSubMenu->TrackPopupMenu(TPM_RIGHTALIGN|TPM_LEFTBUTTON |TPM_RIGHTBUTTON, pos.x, pos.y, m_NotificationWnd, NULL); } else if (LParam == WM_LBUTTONDOWN) {//左键单击恢复窗口 m_NotificationWnd->ShowWindow(SW_SHOW);//恢复窗口 m_NotificationWnd->SetForegroundWindow();//放置在前面 } else if (LParam == WM_LBUTTONDBLCLK) {//左键双击执行缺省菜单项 m_NotificationWnd->SendMessage(WM_COMMAND, pSubMenu->GetMenuItemID(0), 0); } return 1L; } BOOL CTrayIcon::SetTipText(LPCTSTR lpszTip) {//设置提示文字 if (!m_IconExist) return FALSE; _tcscpy(m_nid.szTip, lpszTip); m_nid.uFlags |= NIF_TIP; return Shell_NotifyIcon(NIM_MODIFY, &m_nid); } BOOL CTrayIcon::SetTipText(UINT nID) {//设置提示文字 CString szTip; VERIFY(szTip.LoadString(nID)); return SetTipText(szTip); } BOOL CTrayIcon::ChangeIcon(HICON hIcon) {//更改图标 if (!m_IconExist) return FALSE; m_nid.hIcon = hIcon; m_nid.uFlags |= NIF_ICON; return Shell_NotifyIcon(NIM_MODIFY, &m_nid); } BOOL CTrayIcon::ChangeIcon(UINT nID) {//更改图标 HICON hIcon = AfxGetApp()->LoadIcon(nID); return ChangeIcon(hIcon); } BOOL CTrayIcon::ChangeIcon(LPCTSTR lpszIconName) {//更改图标 HICON hIcon = AfxGetApp()->LoadIcon(lpszIconName); return ChangeIcon(hIcon); } BOOL CTrayIcon::ChangeStandardIcon(LPCTSTR lpszIconName) {//更改为标准图标 HICON hIcon = AfxGetApp()->LoadStandardIcon(lpszIconName); return ChangeIcon(hIcon); } BOOL CTrayIcon::SetNotificationWnd(CWnd * pNotifyWnd) {//设置接受回调消息的窗口 if (!m_IconExist) return FALSE; //确定窗口是有效的 ASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->GetSafeHwnd())); m_NotificationWnd = pNotifyWnd; m_nid.hWnd = pNotifyWnd->GetSafeHwnd(); m_nid.uFlags |= NIF_MESSAGE; return Shell_NotifyIcon(NIM_MODIFY, &m_nid); } CWnd* CTrayIcon::GetNotificationWnd() const {//返回接受回调消息的窗口 return m_NotificationWnd; } 三点补充: 关于使用回调消息的补充说明: 首先,在MainFrm.cpp中加入自己的消息代码; // MainFrm.cpp : implementation of the CMainFrame class // #define MYWM_ICONNOTIFY WM_USER + 10//定义自己的消息代码 第二步增加消息映射和函数声明,对于自定义消息不能由ClassWizard添加消息映射,只能手工添加。 // MainFrm.cpp : implementation of the CMainFrame class BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) //{{AFX_MSG_MAP(CMainFrame) //其他的消息映射 ...... //}}AFX_MSG_MAP ON_MESSAGE(WM_ICONNOTIFY,OnNotify) END_MESSAGE_MAP() 并且在头文件中添加函数声明 // MainFrm.h afx_msg LRESULT OnNotify(WPARAM WParam, LPARAM LParam); 第三步增加消息处理函数定义 LRESULT CMainFrame::OnNotify(WPARAM WParam, LPARAM LParam) { return trayicon.OnNotify(WParam, LParam);//调用CTrayIcon类的处理函数 } 如何隐藏任务栏上的按钮: 可以使用下列两种方法: 1.在CreateWindowEx函数中使用WS_EX_TOOLWINDOW窗口式样(相反的如果要确保应用程序在任务栏上生成按钮,可以使用WS_EX_APPWINDOW窗口式样)。 The problem with this is that the window decorations are as for a small floating toolbar, which isn't normally what's wanted. 2.生成一个空的隐藏的top-level窗口,并使其作为可视窗口的父窗口。 3.在应用程序的InitInstance()函数中使用SW_HIDE式样调用ShowWindow()函数。 //pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->ShowWindow(SW_HIDE); pMainFrame->UpdateWindow(); 如何动画任务栏上的图标: 在TrayIcon类中加入下列两个函数: BOOL CTrayIcon::SetAnimateIcons(HICON* hIcon, UINT Number) {//设置动画图标 ASSERT(Number >= 2);//图标必须为两个以上 ASSERT(hIcon);//图标必须不为空 m_AnimateIcons = new HICON[Number]; CopyMemory(m_AnimateIcons, hIcon, Number * sizeof(HICON)); m_AnimateIconsNumber = Number; return TRUE; } BOOL CTrayIcon::Animate(UINT Index) {//动画TrayIcon UINT i = Index % m_AnimateIconsNumber; return ChangeIcon(m_AnimateIcons[i]); } 在应用程序中添加相应的菜单和函数(请参考下面的例子): void CMainFrame::OnMenuAnimate() {//动画TrayIcon,设置图标及定时器 SetTimer(1, 500, NULL); HICON hIcon[3]; hIcon[0] = AfxGetApp()->LoadIcon(IDR_MAINFRAME); hIcon[1] = AfxGetApp()->LoadIcon(IDR_MYTURNTYPE); hIcon[2] = AfxGetApp()->LoadStandardIcon(IDI_HAND); trayicon.SetAnimateIcons(hIcon, 3); } void CMainFrame::OnTimer(UINT nIDEvent) {//动画TrayIcon UINT static i; i += 1; trayicon.Animate(i); CMDIFrameWnd::OnTimer(nIDEvent); } |