VC6.0下實現系統托盤的氣泡提示窗口
Windows中與托盤圖標相關的提示有兩類:一類是傳統的信息提示方式,當光標移到圖標上時顯示;另一類是新式的信息提示即氣球提示,它是由你的程序來控制顯示。氣球提示有點像連環漫畫中的文字氣球。
首先需要更新一下VC6.0的SDK(ShellAPI.h文件)
使用下面是這個結構的定義的最新版本(For IE5.0+),其中已經加入了新的成員:
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (_WIN32_IE < 0x0500)
WCHAR szTip[64];
#else
WCHAR szTip[128];
#endif
#if (_WIN32_IE >= 0x0500)
DWORD dwState;
DWORD dwStateMask;
WCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
} DUMMYUNIONNAME;
WCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
} NOTIFYICONDATA, *PNOTIFYICONDATA;
1.把ShellAPI.h文件中的關于任務欄提示的函數和常量替換為下面的內容(重要):
屏蔽掉以前的源文件,如下
////
//// Tray notification definitions
////
////
//// End Tray Notification Icons
////
2.在次添加如下的新文件
//start
////
//// Tray notification definitions
////
typedef struct _NOTIFYICONDATAA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (_WIN32_IE < 0x0500)
CHAR szTip[64];
#else
CHAR szTip[128];
#endif
#if (_WIN32_IE >= 0x0500)
DWORD dwState;
DWORD dwStateMask;
CHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
} DUMMYUNIONNAME;
CHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (_WIN32_IE >= 0x600)
GUID guidItem;
#endif
} NOTIFYICONDATAA, *PNOTIFYICONDATAA;
typedef struct _NOTIFYICONDATAW {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (_WIN32_IE < 0x0500)
WCHAR szTip[64];
#else
WCHAR szTip[128];
#endif
#if (_WIN32_IE >= 0x0500)
DWORD dwState;
DWORD dwStateMask;
WCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
} DUMMYUNIONNAME;
WCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (_WIN32_IE >= 0x600)
GUID guidItem;
#endif
} NOTIFYICONDATAW, *PNOTIFYICONDATAW;
#ifdef UNICODE
typedef NOTIFYICONDATAW NOTIFYICONDATA;
typedef PNOTIFYICONDATAW PNOTIFYICONDATA;
#else
typedef NOTIFYICONDATAA NOTIFYICONDATA;
typedef PNOTIFYICONDATAA PNOTIFYICONDATA;
#endif // UNICODE
#define NOTIFYICONDATAA_V1_SIZE FIELD_OFFSET(NOTIFYICONDATAA, szTip[64])
#define NOTIFYICONDATAW_V1_SIZE FIELD_OFFSET(NOTIFYICONDATAW, szTip[64])
#ifdef UNICODE
#define NOTIFYICONDATA_V1_SIZE NOTIFYICONDATAW_V1_SIZE
#else
#define NOTIFYICONDATA_V1_SIZE NOTIFYICONDATAA_V1_SIZE
#endif
#define NOTIFYICONDATAA_V2_SIZE FIELD_OFFSET(NOTIFYICONDATAA, guidItem)
#define NOTIFYICONDATAW_V2_SIZE FIELD_OFFSET(NOTIFYICONDATAW, guidItem)
#ifdef UNICODE
#define NOTIFYICONDATA_V2_SIZE NOTIFYICONDATAW_V2_SIZE
#else
#define NOTIFYICONDATA_V2_SIZE NOTIFYICONDATAA_V2_SIZE
#endif
#if (_WIN32_IE >= 0x0500)
#define NIN_SELECT (WM_USER + 0)
#define NINF_KEY 0x1
#define NIN_KEYSELECT (NIN_SELECT | NINF_KEY)
#endif
#if (_WIN32_IE >= 0x0501)
#define NIN_BALLOONSHOW (WM_USER + 2)
#define NIN_BALLOONHIDE (WM_USER + 3)
#define NIN_BALLOONTIMEOUT (WM_USER + 4)
#define NIN_BALLOONUSERCLICK (WM_USER + 5)
#endif
#define NIM_ADD 0x00000000
#define NIM_MODIFY 0x00000001
#define NIM_DELETE 0x00000002
#if (_WIN32_IE >= 0x0500)
#define NIM_SETFOCUS 0x00000003
#define NIM_SETVERSION 0x00000004
#define NOTIFYICON_VERSION 3
#endif
#define NIF_MESSAGE 0x00000001
#define NIF_ICON 0x00000002
#define NIF_TIP 0x00000004
#if (_WIN32_IE >= 0x0500)
#define NIF_STATE 0x00000008
#define NIF_INFO 0x00000010
#endif
#if (_WIN32_IE >= 0x600)
#define NIF_GUID 0x00000020
#endif
#if (_WIN32_IE >= 0x0500)
#define NIS_HIDDEN 0x00000001
#define NIS_SHAREDICON 0x00000002
// says this is the source of a shared icon
// Notify Icon Infotip flags
#define NIIF_NONE 0x00000000
// icon flags are mutually exclusive
// and take only the lowest 2 bits
#define NIIF_INFO 0x00000001
#define NIIF_WARNING 0x00000002
#define NIIF_ERROR 0x00000003
#define NIIF_ICON_MASK 0x0000000F
#if (_WIN32_IE >= 0x0501)
#define NIIF_NOSOUND 0x00000010
#endif
#endif
SHSTDAPI_(BOOL) Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData);
SHSTDAPI_(BOOL) Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData);
#ifdef UNICODE
#define Shell_NotifyIcon Shell_NotifyIconW
#else
#define Shell_NotifyIcon Shell_NotifyIconA
#endif // !UNICODE
////
//// End Tray Notification Icons
////
///end
3.在stdafx.h文件中添加:
#ifndef _WIN32_IE // 允許使用 IE 4.0 或更高版本的特定功能。
#define _WIN32_IE 0x0500 //為 IE 5.0 及更新版本改變為適當的值。
#endif
4.添加函數(需要先添加系統托盤圖標)
//添加成員變量
NOTIFYICONDATA m_nid;
BOOL CMyCatchFun::ShowBalloonTip(
CWnd* pWnd,
LPCTSTR szMsg,
LPCTSTR szTitle,
UINT uTimeout,
UINT uCallbackMessage
)
{
m_nid.cbSize = sizeof(NOTIFYICONDATA);
m_nid.uTimeout = uTimeout;
m_nid.uVersion = NOTIFYICON_VERSION;
m_nid.dwInfoFlags = NIIF_INFO; //如果前面修改錯誤,這里會發生"未定義標識符"錯誤
m_nid.uFlags = NIF_MESSAGE|NIF_INFO|NIF_ICON;
m_nid.uID = IDR_MAINFRAME;
m_nid.hWnd = pWnd->m_hWnd;
m_nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_nid.uCallbackMessage = uCallbackMessage;
_tcscpy(m_nid.szInfoTitle,szTitle);
_tcscpy(m_nid.szInfo,szMsg);
return Shell_NotifyIcon(NIM_MODIFY,&m_nid);
}
5.修改氣球提示信息
void CMyDlg::OnButton1()
{
_tcscpy(m_nid.szInfoTitle,"提醒你");
_tcscpy(m_nid.szInfo,"內容改變");
m_nid.uTimeout=1000;
m_nid.uVersion=NOTIFYICON_VERSION;
Shell_NotifyIcon(NIM_MODIFY,&m_nid);
}
在NOTIFYICONDATA.uFlags中的標志之一是NIF_TIP,用它來設置傳統的信息提示,即鼠標要移動到圖標上。新的標志NIF_INFO(由于_WIN32_IE >= 0x0500條件定義,因此在編譯時,請注意包含最新版本的頭文件shellapi.h,并保證鏈接最新版本的庫文件shell32.lib,分發程序時用最新版本的運行時動態鏈接庫shell32.dll)便是為顯示氣球提示所用的。也就是說,要顯示氣球提示,那么在調用Shell_NotifyIcon函數時必須用NIF_INFO標志。提示文本填入szInfo域,標題文本填入szInfoTitle。你甚至可以在NOTIFYICONDATA.uTimeout中設置一個超時時間,當經過指定的毫秒數之后,氣球提示自動隱藏。
為了示范氣球提示的實現原理,我對本文前面兩個部分的例子以及CTrayIcon類進行了修改。CTrayIcon類中添加了一個新的方法ShowBalloonTip,這個方法有兩個重載函數,既可以用文本串來調用,也可以用資源ID來調用。用資源ID時,可以有選擇地加載文本串,并調用ShowBalloonTip的文本串版本,原型如下:
BOOL CTrayIcon::ShowBalloonTip(LPCTSTR szMsg, LPCTSTR szTitle, UINT uTimeout, DWORD dwInfoFlags) { m_nid.cbSize=sizeof(NOTIFYICONDATA); m_nid.uFlags = NIF_INFO; m_nid.uTimeout = uTimeout; m_nid.dwInfoFlags = dwInfoFlags; strcpy(m_nid.szInfo,szMsg ? szMsg : _T("")); strcpy(m_nid.szInfoTitle,szTitle ? szTitle : _T("")); return Shell_NotifyIcon(NIM_MODIFY, &m_nid); }
這個函數很容易理解,同時也夠繁瑣的,要把文本串載緩沖里拷來拷去。缺省的dwInfoFlags設置為NIIF_INFO,在文本旁邊顯示信息圖標;其它可能的標志是NIIF_ERROR——表示出錯,NIIF_WARNING——表示警告,NIIF_NONE——沒有圖標。有關修改后的CTrayIcon以及TrayTest3源代碼請下載本文的例子。
只有一種方法可以顯示氣球提示(Shell_NotifyIcon),但終止的方法有多種。用戶可以在氣球上單擊鼠標,也可以單擊關閉按鈕(在Windows 2000里沒有關閉按鈕,如圖一),或者Windows用超時機制來終止氣球提示。那么是如何知道所發生的事件是什么呢?每當創建托盤圖標時,你可以提供一個HWND和消息ID來接收事件發生的通知。如果用戶單擊氣球提示,Windows發送NIN_BALLOONUSERCLICK;如果超時或者單擊關閉按鈕,Windows則發送NIN_BALLOONTIMEOUT。就我所知,目前還沒有辦法區分是超時還是單擊了關閉按鈕。下表中列出的是所有與氣球提示相關的通知消息:
通知消息
|
描述
|
NIN_BALLOONSHOW |
顯示氣球提示時發送 |
NIN_BALLOONHIDE |
氣球提示消失時發送;例如,當圖標被刪除,如果因為超時或是用戶單擊鼠標氣球消失,此消息不會被發送 |
NIN_BALLOONTIMEOUT |
當由于超時或者用戶單擊氣球上的關閉按鈕(X),使氣球消失時發送此消息 |
NIN_BALLOONUSERCLICK |
當用戶在氣球提示上或托盤圖標上單擊鼠標(此時氣球處于顯示狀態)時發送此消息 |
在測試過程中,我發現一個奇特的現象:在Windows XP中,只要你的托盤程序擁有焦點,氣球提示便不會超時。顯然,你只有轉到其它應用程序,才能啟動計時器。在Windows 2000里好像沒有這個問題。