当前位置:首页 > 工业控制 > 电子设计自动化
[导读]自从微软公司推出Windows 95操作系统以来,系统托盘应用作为一种极具吸引力的用户界面设计深受广大用户的喜爱。使用系统托盘作为用户界面的Windows应用程序数不胜数,比如"金山词霸"、"Winamp"、"RealPlayer"等等。

自从微软公司推出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==0uCbMsg>=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_OVERLAPPEDWINDOWFWS_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中设置一个超时时间,当经过指定的毫秒数之后,气球提示自动隐藏.

欢迎转载,信息来自维库电子市场网(www.dzsc.com)



来源:ks990次

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

仿真的概念其实使用非常广,最终的含义就是使用可控的手段来模仿真实的情况。在嵌入式系统的设计中,仿真应用的范围主要集中在对程序的仿真上。

关键字: 单片机 仿真器 程序

步进电动机是将电脉冲激励信号转换成相应的角位移或线位移的离散值控制电动机,这种电动机每当输入一个电脉冲就动一步,所以又称脉冲电动机。

关键字: 步进电机 正反转 程序

KeilμVision4是Keil软件公司为8051系列微控制器及其兼容产品设计的集成式软件开发环境。μVision4集成了C51编译器和A51汇编器,其界面类似于Microsoft VS,支持C语言和汇编语言程序的编写...

关键字: 程序 编译 链接

双方各执一词,谁也不退让,吴雄昂的身份成为薛定谔的猫:在Arm公司眼中,他已经被罢免一切职位;在安谋中国声明里,吴仍然一切照常。这种叠加态或许还会持续一段时间,但叠加态应该很快就会塌缩成一个确定结果。同样,安谋中国董事会...

关键字: ARM 中国董事会 程序

除了指令空间,自定义指令对应的程序出入口也有严格限制。自定义指令在使用过程中出现任何错误时,Arm的工具链都能及时对其进行识别、提取,并且进行相应的控制。目前也已经有第三方编译器,可以识别自定义指令集可能会出现的错误。专...

关键字: 指令空间 程序 ARM

如果说一众美国科技公司遵循特朗普命令断供华为,还算某种程度上维护所谓程序正义不得已而为之;那么各路本应对政治保持中立的国际技术标准组织,先后宣布剔除华为成员资格,绝对算助纣为虐;而联邦快递将华为委托寄送目的地为中国的快递...

关键字: 快递 华为 程序

在软件开发过程中,我们希望软件可以运行无误。但是常常事与愿违,程序经常跑飞,或者卡死。原因有很多,有可能是因为软件系统设计的原因,或者外部传感器的失效,再或者是程序的Bug等。为了防止程序在出现问题之后,可以顺利复位和重...

关键字: 软件 程序 传感器

进程是程序的执行过程。程序是静态的,是存在于外存之中的,电脑关机后依然存在。进程是动态的,是存在于内存之中的,是程序的执行过程,电脑关机后就不存在进程了。进程的内容来源于程序,进程的启动过程就是把程序从外存加载到内存的过...

关键字: 程序 静态 操作系统

摘 要:结合车联网高峰论坛上的一些最新观点,对车联网的一些新进展作了介绍。主要包括大数据和云计算在车联网 的应用,车联网的电商化及互联网化趋势。车联网的商业模式需要突破,跨界合作和服务创新是一种有益的尝试。认为只有开放的...

关键字: 车联网 进展 大数据 电商 程序 开放

这几天打算复习下stm32有关的硬件资源,就想着从最开始做起,熟悉下当初所学的知识。学习stm32最初都基本是从流水灯开始的,今天就开始点亮流水灯。

关键字: STM32 程序 编程
关闭
关闭