利用Visual C++实现系统托盘程序

 
利用 Visual C++ 实现系统托盘程序
自从微软公司推出 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 有效
#define NIF_ICON 0x2 //
表示 hIcon 有效
#define NIF_TIP 0x4 //
表示 szTip 有效
在该结构的成员中, cbSize 为该结构所占的字节数, hWnd 为接受该图标所发出的消息的窗口的句柄(鼠标在任务栏上程序图标上动作时图标将发出消息,这个消息用户要自己定义), uID 为被显示图标的 ID uFlags 指明其余的几个成员( hIcon uCallBackMessage szTip )的值是否有效, uCallbackMessage 为一个用户自定义的消息,当用户在该图标上作用一些鼠标动作时,图标将向应用程序的主框架窗口( hWnd 成员中指定的窗口)发出该消息,为了使程序的主框架得到该通知消息,需要设置 NOTIFYICONDATA 结构的 flag 成员的值为 NIF_MESSAGE hIcon 为将在任务栏上显示的图标句柄, 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)
   //}}AFX_VIRTUAL
   // Implementation
  public:
   virtual ~CMainFrame();
   #ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
   #endif
  protected: // control bar embedded members
   CStatusBar m_wndStatusBar;
   CTrayIcon m_trayIcon; // my tray icon
   CEdit m_wndEdit; // to display tray notifications
   int m_iWhichIcon; // which HICON to use
   BOOL m_bShutdown; // OK to terminate TRAYTEST
   BOOL m_bShowTrayNotifications; // display info in main window
   // Generated message map functions
  protected:
   //{{AFX_MSG(CMainFrame)
    afx_msg LRESULT OnTrayNotification(WPARAM wp, LPARAM lp);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnToggleIcon();
    afx_msg void OnViewClear();
    afx_msg void OnViewNotifications();
    afx_msg void OnUpdateViewClear(CCmdUI* pCmdUI);
    afx_msg void OnUpdateViewNotifications(CCmdUI* pCmdUI);
    afx_msg void OnClose();
    afx_msg void OnAppOpen();
    afx_msg void OnAppSuspend();
    // NOTE - the ClassWizard will add and remove member functions here.
    // DO NOT EDIT what you see in these blocks of generated code!
   //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};

///CMainFrm.cpp
#include "stdafx.h"
#include "TrayTest.h"
#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
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)
   //}}AFX_VIRTUAL
   // Implementation
  public:
   virtual ~CMainFrame();
   #ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
   #endif
  protected: // control bar embedded members
   CStatusBar m_wndStatusBar;
   CTrayIcon m_trayIcon; // my tray icon
   CEdit m_wndEdit; // to display tray notifications
   int m_iWhichIcon; // which HICON to use
   BOOL m_bShutdown; // OK to terminate TRAYTEST
   BOOL m_bShowTrayNotifications; // display info in main window
   // Generated message map functions
  protected:
   //{{AFX_MSG(CMainFrame)
    afx_msg LRESULT OnTrayNotification(WPARAM wp, LPARAM lp);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnToggleIcon();
    afx_msg void OnViewClear();
    afx_msg void OnViewNotifications();
    afx_msg void OnUpdateViewClear(CCmdUI* pCmdUI);
    afx_msg void OnUpdateViewNotifications(CCmdUI* pCmdUI);
    afx_msg void OnClose();
    afx_msg void OnAppOpen();
    afx_msg void OnAppSuspend();
    // NOTE - the ClassWizard will add and remove member functions here.
    // DO NOT EDIT what you see in these blocks of generated code!
   //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};

///CMainFrm.cpp
#include "stdafx.h"
#include "TrayTest.h"
#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
  CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
  CFrameWnd::Dump(dc);
}

#endif //_DEBUG
///
BOOL CMyApp::InitInstance()
{
  // 在应用程序初始化函数中将程序的主框架隐藏起来;
  #ifdef _AFXDLL
   Enable3dControls(); // Call this when using MFC in a shared DLL
  #else
   Enable3dControlsStatic(); // Call this when linking to MFC statically
  #endif
  SetRegistryKey(_T("Local AppWizard-Generated Applications"));
  CMainFrame* pFrame = new CMainFrame;
  m_pMainWnd = pFrame;
  pFrame->LoadFrame(IDR_MAINFRAME,
  WS_OVERLAPPEDWINDOW   FWS_ADDTOTITLE, NULL, NULL);
  pFrame->ShowWindow(SW_HIDE);
  pFrame->UpdateWindow();
  return TRUE;
}

  四、小结

  托盘程序的信息提示通常是将鼠标光标移到托盘图标上之后, Windows 会发送消息给托盘程序,从而显示提示信息 --Tooltip 。但在 Windows XP 中我们还看到有些系统托盘程序是自动显示 ToolTips 信息的,也就是说不用将鼠标光标移到托盘图标上便可显示 ToolTips ,此类新式的信息提示一般称为气球提示,它是由你的程序来控制显示。气球提示为托盘程序提供了一种非打扰式的方法通知用户发生了某件事情。但是如何让气球提示显示出来呢?其实所有的托盘图标行为都是通过一个单纯的 API 函数 Shell_NotifyIcon 来操作的。你可以利用这个函数的参数 NOTIFYICONDATA 结构,这个结构来告诉 Windows 你想要做什么。下面是这个结构的定义的最新版本( 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;

  在 NOTIFYICONDATA.uFlags 中的标志之一是 NIF_TIP ,用它来设置传统的信息提示,即鼠标要移动到图标上。新的标志 NIF_INFO (由于 _WIN32_IE >= 0x0500 条件定义,因此在编译时,请注意包含最新版本的头文件 shellapi.h ,并保证链接最新版本的库文件 shell32.lib ,分发程序时用最新版本的运行时动态链接库 shell32.dll )便是为显示气球提示所用的。也就是说,要显示气球提示,那么在调用 Shell_NotifyIcon 函数时必须用 NIF_INFO 标志。提示文本填入 szInfo 域,标题文本填入 szInfoTitle 。你甚至可以在 NOTIFYICONDATA.uTimeout 中设置一个超时时间,当经过指定的毫秒数之后,气球提示自动隐藏
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值