自從微軟公司推出Windows 95操作系統以來,系統托盤應用作為一種極具吸引力的用戶界面設計深受廣大用戶的喜愛。使用系統托盤作為用戶界面的Windows應用程序數不勝數,比如"金山詞霸"、"Winamp"、"RealPlayer"等等。
這些程序運行時不顯示運行窗口,只在任務欄上顯示一個圖標,表示程序正在運行,用戶可以通過鼠標與應用程序交互,程序開發人員有時也需要編制一些僅在後台運行的類似程序,為了不干擾前台程序的運行界面和不顯示不必要的窗口,應使程序運行時的主窗口不可見。同時將一個圖標顯示在任務欄右端靜態通告區中並響應用戶的鼠標動作。本實例就介紹Visual C++開發這類程序的設計方法,該程序編譯運行後,如果雙擊托盤圖標,程序會彈出一個消息列表窗口,只要鼠標在托盤圖標上移動或點擊(無論是左右鍵的單擊或雙擊),產生的消息都會顯示在這個窗口裡;當鼠標光標移到托盤圖標上時,在圖標附近會顯示提示信息;單擊右鍵時彈出上下文菜單,這個菜單中應包含打開屬性頁的命令或者打開與圖標相關的其它窗口的命令,另外,該程序還可以動態的改變托盤的圖標。參照這個例子,相信讀者能輕松自如地在自己的程序中應用系統托盤。
一、實現方法
為了實現拖盤程序,首先要使程序的主窗口不可見,這點實現起來十分容易,只要調用ShowWindow(SW_HIDE)就可以了,本實例采用的就是這種方法,還有一種思路是通過分別設置主邊框窗口的風格和擴展風格來隱藏主框架:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style =WS_POPUP;//使主窗口不可見;
cs.dwExStyle =WS_EX_TOOLWINDOW;//不顯示任務按鈕;
return CFrameWnd::PreCreateWindow(cs);
}
在任務條上顯示圖標是利用系統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 有效
在該結構的成員中,cbSize為該結構所占的字節數,hWnd為接受該圖標所發出的消息的窗口的句柄(鼠標在任務欄上程序圖標上動作時圖標將發出消息,這個消息用戶要自己定義),uID為被顯示圖標的ID,uFlags指明其余的幾個成員(hIcon、uCallBackMessage和szTip)的值是否有效,uCallbackMessage為一個用戶自定義的消息,當用戶在該圖標上作用一些鼠標動作時,圖標將向應用程序的主框架窗口(hWnd成員中指定的窗口)發出該消息,為了使程序的主框架得到該通知消息,需要設置NOTIFYICONDATA 結構的flag成員的值為NIF_MESSAGE。hIcon為將在任務欄上顯示的圖標句柄,szTip鼠標停留在該圖標上時顯示的提示字符串。
#define NIF_ICON 0x2 // 表示hIcon 有效
#define NIF_TIP 0x4 // 表示szTip 有效
盡管Shell_NotifyIcon函數簡單實用,但它畢竟是個Win32 API,為此本實例將它封裝在了一個C++類中,這個類叫做CTrayIcon,有了它,托盤編程會更加輕松自如,因為它隱藏了NOTIFYICONDATA、消息代碼、標志以及一些繁瑣的細節。
二、編程步驟
1、 啟動Visual C++6.0,生成一個單文檔的應用程序TrayTest,取消文檔視圖支持;
2、 在CMainFrame類中添加自定義消息#define WM_MY_TRAY_NOTIFICATION WM_USER+0,並在該類中為此自定義消息手動添加消息映射ON_MESSAGE(WM_MY_TRAY_NOTIFICATION, OnTrayNotification)和消息響應函數afx_msg LRESULT OnTrayNotification(WPARAM wp, LPARAM lp);
3、 設計二個圖標添加到項目中,其ID標志分別為"IDI_MYICON"、"IDI_MYICON2",作為托盤顯示時的圖標;
4、 在CMainFrame類中添加下述變量: CTrayIcon m_trayIcon(用來操作圖標的類對象)、CEdit m_wndEdit(編輯框用來顯示所跟蹤到的鼠標消息)、int m_iWhichIcon(決定當前托盤使用哪個圖標)、BOOL m_bShutdown(是否關閉當前拖盤程序標志)、BOOL m_bShowTrayNotifications(是否顯示托盤消息標志);
5、 為程序的IDR_MAINFRAME添加處理菜單項和托盤的上下文菜單IDI_TRAYICON(具體的菜單項的標題和ID標志符參見代碼部分),然後使用Class Wizard為各個菜單項添加處理函數;
6、 添加代碼,編譯運行程序。
////////////////////////////////////////////////CTrayIcon類的頭文件;
#ifndef _TRAYICON_H
#define _TRAYICON_H
class CTrayIcon : public CCmdTarget {
protected:
DECLARE_DYNAMIC(CTrayIcon)
NOTIFYICONDATA m_nid; // struct for Shell_NotifyIcon args
public:
CTrayIcon(UINT uID);
~CTrayIcon();
// Call this to receive tray notifications
void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);
BOOL SetIcon(UINT uID); // main variant you want to use
BOOL SetIcon(HICON hicon, LPCSTR lpTip);
BOOL SetIcon(LPCTSTR lpResName, LPCSTR lpTip)
{ return SetIcon(lpResName ?
AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); }
BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCSTR lpTip)
{ return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); }
virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
};
#endif
///////////////////////////////////////////////////CTrayIcon類的.CPP文件
#include "stdafx.h"
#include "trayicon.h"
#include // for AfxLoadString
IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)
CTrayIcon::CTrayIcon(UINT uID)
{
memset(&m_nid, 0 , sizeof(m_nid)); // Initialize NOTIFYICONDATA
m_nid.cbSize = sizeof(m_nid);
m_nid.uID = uID; // never changes after construction
AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
// Use resource string as tip if there is one
}
CTrayIcon::~CTrayIcon()
{
SetIcon(0); // remove icon from system tray
}
void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
// Set notification window. It must created already.
ASSERT(pNotifyWnd==NULL ::IsWindow(pNotifyWnd->GetSafeHwnd()));
m_nid.hWnd = pNotifyWnd->GetSafeHwnd();
ASSERT(uCbMsg==0 uCbMsg>=WM_USER);
m_nid.uCallbackMessage = uCbMsg;
}
BOOL CTrayIcon::SetIcon(UINT uID)
{
// Sets both the icon and tooltip from resource ID ,To remove the icon, call SetIcon(0)
HICON hicon=NULL;
if (uID) {
AfxLoadString(uID, m_nid.szTip, sizeof(m_nid.szTip));
hicon = AfxGetApp()->LoadIcon(uID);
}
return SetIcon(hicon, NULL);
}
BOOL CTrayIcon::SetIcon(HICON hicon, LPCSTR lpTip)
{
// Common SetIcon for all overloads.
UINT msg;
m_nid.uFlags = 0;
if (hicon) {
// Set the icon
msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
m_nid.hIcon = hicon; // Add or replace icon in system tray
m_nid.uFlags = NIF_ICON;
} else {
if (m_nid.hIcon==NULL) // remove icon from tray
return TRUE; // already deleted
msg = NIM_DELETE;
}
if (lpTip) // Use the tip, if any
strncpy(m_nid.szTip, lpTip, sizeof(m_nid.szTip));
if (m_nid.szTip[0])
m_nid.uFlags = NIF_TIP;
if (m_nid.uCallbackMessage && m_nid.hWnd) // Use callback if any
m_nid.uFlags = NIF_MESSAGE;
BOOL bRet = Shell_NotifyIcon(msg, &m_nid); // Do it
if (msg==NIM_DELETE !bRet)
m_nid.hIcon = NULL; // failed
return bRet;
}
LRESULT CTrayIcon::OnTrayNotification(WPARAM wID, LPARAM lEvent)
{
if (wID!=m_nid.uID (lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
return 0;
CMenu menu;//裝載上下文菜單;
if (!menu.LoadMenu(m_nid.uID))
return 0;
CMenu* pSubMenu = menu.GetSubMenu(0);
if (!pSubMenu)
return 0;
if (lEvent==WM_RBUTTONUP) {//設置第一個菜單項為默認菜單項目
::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);
//將當前菜單作為上下文菜單;
CPoint mouse;
GetCursorPos(&mouse);
::SetForegroundWindow(m_nid.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0,m_nid.hWnd, NULL);
} else // double click: execute first menu item
::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);
return 1;
}
///////////////////////////////// MainFrm.h : interface of the CMainFrame class
#if !defined(AFX_MAINFRM_H__9ED70A69_C975_4F20_9D4E_B2877E3575D0__INCLUDED_)
#define AFX_MAINFRM_H__9ED70A69_C975_4F20_9D4E_B2877E3575D0__INCLUDED_
#if _MSC_VER >1000
#pragma once
#endif // _MSC_VER >1000
#include "trayicon.h"
class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
// Attributes
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
/
*