現在比較流行的軟體都會在系統托盤上顯示一個表徵圖,左鍵點擊該表徵圖時可以顯示/隱藏軟體主介面,節省工作列的空間;右鍵點擊該表徵圖會出現一個菜單,顯示一些常用的功能。那麼這是怎麼實現的呢?其實很簡單,只需要一個API函數就搞定了,跟我一起看吧~
首先看一個結構體:
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
TCHAR szTip[64];
DWORD dwState; //Version 5.0
DWORD dwStateMask; //Version 5.0
TCHAR szInfo[256]; //Version 5.0
union {
UINT uTimeout; //Version 5.0
UINT uVersion; //Version 5.0
} DUMMYUNIONNAME;
TCHAR szInfoTitle[64]; //Version 5.0
DWORD dwInfoFlags; //Version 5.0
} NOTIFYICONDATA, *PNOTIFYICONDATA;
使用該結構指定托盤表徵圖的相關資訊,其中Version 5.0的部分表示的是Shell DLL的版本號碼,可以用DllGetVersion獲得當前系統的DLL版本。出於相容的考慮,該部分很少使用。
cbSize: NOTIFYICONDATA結構的大小,sizeof(NOTIFYICONDATA)
hWnd:接收托盤通知的視窗控制代碼
uID:托盤表徵圖的ID號,該ID與hWnd一起唯一標識了一個托盤表徵圖的身份,因此該ID號不能重複,一般使用與該表徵圖相關聯的菜單的ID
uFlags:為下列值的組合
NIF_ICON:表示結構中的hIcon有效
NIF_MESSAGE:表示結構中的uCallbackMessage有效
NIF_TIP:表示結構中的szTip有效
NIF_STATE:表示結構中的dwState和dwStateMask有效
NIF_INFO:使用氣球型的托盤提示代替傳統的方框型托盤提示.結構中的szInfo, uTimeout, szInfoTitle,和dwInfoFlags有效
uCallbackMessage:托盤訊息,當托盤地區有滑鼠事件(如滑鼠移動,單擊等)產生時,會向接收視窗發送該訊息,進行相應的處理
hIcon:托盤表徵圖控制代碼
szTip:托盤提示字元
好了,就介紹這麼多,下面接收這個API函數Shell_NotifyIcon:
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA pnid
);
dwMessage:為下列值之一
NIM_ADD:添加托盤表徵圖
NIM_DELETE:刪除圖盤表徵圖
NIM_MODIFY:修改托盤表徵圖
NIM_SETFOCUS:Version 5.0
NIM_SETVERSION:Version 5.0
pnid:NOTIFYICONDATA結構的指標
好了,下面言歸正傳,開始著手編程了~
我們分為以下幾步逐步分析:
初始化
添加/修改/移除表徵圖
添加托盤訊息響應函數
添加菜單訊息處理函數
(1)初始化
首先要聲明一個NOTIFYICONDATA成員變數:
NOTIFYICONDATA m_nid;
然後在資源面板中添加一個菜單資源IDR_TRAYMENU和一個表徵圖資源IDI_RED
由於我們要處理滑鼠訊息,因此先定義一個使用者自訂訊息:
#define UM_ TRAYNOTIFICATION (WM_USER+100)
然後對該結構的成員進行初始化:
void InitTray()
{
//初始化m_nid
m_nid.cbSize = sizeof(NOTIFYICONDATA);
m_nid.hWnd = this->m_hWnd;
m_nid.uID = IDR_TRAYMENU;
m_nid.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
m_nid.hIcon = AfxGetApp()->LoadIcon(IDI_RED);
strcpy (m_nid.szTip, "我的托盤聽我的");
m_nid.uCallbackMessage = UM_TRAYNOTIFICATION;
}
上面的程式我就不多說了,相信大家都能看懂。
(2)添加/修改/移除表徵圖
這個更簡單,直接調用Shell_NotifyIcon函數就可以了:
//向托盤添加表徵圖
void AddTray()
{
Shell_NotifyIcon(NIM_ADD, &m_nid);
}
//移除托盤表徵圖,在程式退出時一定要記得調用,否則表徵圖會殘留在托盤上
void RemoveTray()
{
Shell_NotifyIcon(NIM_DELETE, &m_nid);
}
//修改表徵圖,QQ登陸時托盤上的動態效果不用我教了吧
void ModifyTray(UINT uId)//參數為要顯示的Icon的ID號
{
m_nid.hIcon = AfxGetApp()->LoadIcon(uId);
Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}
(3)添加托盤訊息響應函數
這個其實就是響應自訂訊息,忘了?好,那我們一起複習一下吧~
首先定義訊息標識符(第1步已經定義過了):
#define UM_ TRAYNOTIFICATION (WM_USER+100)
然後在標頭檔的DECLARE_MESSAGE_MAP()之前定義訊息響應函數:
afx_msg LRESULT OnTrayNotification(WPARAM wId, LPARAM lEvent);
其中兩個參數分別為表徵圖ID號和滑鼠事件
最後在cpp檔案裡實現該函數:
LRESULT CXXXDlg::OnTrayNotification(WPARAM wId, LPARAM lEvent)
{
if(wId!=m_nid.uID
|| (lEvent!=WM_LBUTTONUP && lEvent!=WM_RBUTTONUP))
return 0;
//載入菜單
CMenu menu;
if(!menu.LoadMenu(wId))
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
{
::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);
}
return 1;
}
下面逐句解釋上面這段代碼:
首先判斷髮送該訊息的表徵圖的ID是否等於m_nid中的ID號,再判斷接收到的滑鼠訊息是否為左鍵單擊訊息WM_LBUTTONUP或按右鍵訊息WM_RBUTTONUP,如果不是則不對該訊息進行響應。
然後載入菜單,按右鍵時顯示該菜單。先用LoadMenu()載入頂層菜單,再用GetSubMenu()獲得一級子功能表。
當滑鼠事件為按右鍵時,顯示菜單,否則(即左鍵單擊時)用::SendMessage()向接收視窗發送命令訊息,進行相應的處理。::SetMenuDefaultItem()用於設定菜單的預設選擇項,以粗體顯示。最後用::TrackPopupMenu()函數顯示右鍵菜單。該菜單可能不會像通常那樣馬上消失,這是因為從快顯功能表接收訊息的視窗必須是前景視窗。調用::SetForegroundWindow()函數就可以糾正該錯誤。
(4)添加菜單訊息處理函數
在右鍵菜單中選擇一項後,就會發送命令訊息WM_COMMAND,在ClassWizard裡添加WM_COMMAND的訊息處理函數:
BOOL CZYJReportDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch(LOWORD(wParam))
{
case ID_SHOWHIDE:
ShowWindow(m_bShow?SW_HIDE:SW_SHOW);
m_bShow = !m_bShow;
break;
case ID_EXIT:
RemoveTray();
PostQuitMessage(0);
break;
case ID_ABOUT:
CAboutDlg about;
about.DoModal();
break;
}
return CDialog::OnCommand(wParam, lParam);
}
其中第一個參數wParam的低位元組LOWORD(wParam)標識了所選子功能表的ID,根據所選的功能表項目執行相應的程式。
OK,以上介紹了寫托盤程式的完整過程,是不是很簡單呢?當然托盤編程的內容還遠不止這些,比如Version 5.0的部分,還可以添加很多漂亮的效果,歡迎大家跟我一起探討!