如何通过托盘图标得到该所属进程

jiaofeng 2005-03-31 05:17:20
想要求教两个问题:

首先,如何得到当前所有托盘图标?
还有,如何通过托盘图标得到该所属进程


想知道使用什么函数,要是能给个例程就更感谢了!
谢谢,再次感谢!
...全文
815 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
jiaofeng 2005-04-04
  • 打赏
  • 举报
回复
keiy 真厉害,这100分全是你的了!
柯本 2005-04-04
  • 打赏
  • 举报
回复
进程ID可以得到了,以下是全部程序:
void CTrayDlg::OnButton1()
{
// TODO: Add your control notification handler code here
HWND wd=::FindWindow("Shell_TrayWnd",NULL);
if (wd==NULL)
{
MessageBox("Error1");
return;
}
HWND wtd=FindWindowEx(wd,NULL,"TrayNotifyWnd",NULL);
if (wtd==NULL)
{
MessageBox("Error2");
return;
}
HWND wd1=FindWindowEx(wtd,NULL,"ToolbarWindow32",NULL);
if (wd1==NULL)
{
MessageBox("Error3");
return;
}
DWORD pid;
pid=0;
GetWindowThreadProcessId(wd1,&pid);
if (pid==NULL)
{
MessageBox("Error4");
return;
}

HANDLE hd=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_ALL_ACCESS ,true,pid);
if (hd==NULL)
{
MessageBox("Error6");
return;
}

int num=::SendMessage(wd1,TB_BUTTONCOUNT ,NULL,NULL);
int i;
unsigned long n;
TBBUTTON p,*pp;
CString x;
wchar_t name[256];
unsigned long whd,proid;
CString temp;

TBBUTTON *sp;

sp= (TBBUTTON *)0x20f00;
for(i=0;i<num;i++)
{
::SendMessage(wd1,TB_GETBUTTON,i,(LPARAM)sp);
pp=&p;
ReadProcessMemory(hd,sp,pp,sizeof(p),&n);
// x.Format("%x %x %x %x %x %x",p.iBitmap,p.idCommand,p.fsState,p.fsStyle, p.dwData, p.iString);
name[0]=0;
if (p.iString!=0xffffffff)
{
try
{
ReadProcessMemory(hd,(void *)p.iString,name,255,&n);
name[n]=0;
}
catch(...)
{
}
// x+=" ";
// x+=name;
temp=name;
try
{
whd=0;
ReadProcessMemory(hd,(void *)p.dwData,&whd,4,&n);
}
catch(...)
{
}
proid=0;
GetWindowThreadProcessId((HWND)whd,&proid);
x.Format("位置=%d 名称=%s 窗口句柄=%08x 进程ID=%08x",
i,(LPCTSTR )temp,whd,proid);
m_list.AddString(x);
}


}


}
jiaofeng 2005-04-04
  • 打赏
  • 举报
回复
感谢keiy
先给50分

我试了试在XP里在TrayNotifyWnd和ToolbarWindow32中还有一个"SysPager"

还有一半的问题没解决
如何到它所指向的Process ID或Handle
Kudeet 2005-04-02
  • 打赏
  • 举报
回复
http://community.csdn.net/Expert/topic/3898/3898421.xml?temp=.9289362
柯本 2005-04-02
  • 打赏
  • 举报
回复
经过2天的努力,动用了winhex,spy++等工具,终于有了一个不是很满意的结果,我是利用TBBUTTON来实现的,不过,还是有两个问题
1.SendMessage(wd1,TB_GETBUTTON,i,(LPARAM)sp); 中的sp,居然要是Explorer.exe中的内存,而非当前进程的内存,所以,我只能用winhex找到一个自认为安全的0x20f00,至少在我的win2k下没问题
2.我现只能得到Tray的数量,名称,还是没能得到它所指向的Process ID或Handle
我只是提供一个新的思路,供各位参考
以下是程序及我机上的结果(开发环境win2k+sp4,vc6或BCB)
其中m_list是一 List Box控件
void CGETTrayDlg::OnButton1()

{
// TODO: Add your control notification handler code here
HWND wd=::FindWindow("Shell_TrayWnd",NULL);
if (wd==NULL)
{
MessageBox("Error1");
return;
}
HWND wtd=FindWindowEx(wd,NULL,"TrayNotifyWnd",NULL);
if (wtd==NULL)
{
MessageBox("Error2");
return;
}
HWND wd1=FindWindowEx(wtd,NULL,"ToolbarWindow32",NULL);
if (wd1==NULL)
{
MessageBox("Error3");
return;
}
DWORD pid;
pid=0;
GetWindowThreadProcessId(wd1,&pid);
if (pid==NULL)
{
MessageBox("Error4");
return;
}

HANDLE hd=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_ALL_ACCESS ,true,pid);
if (hd==NULL)
{
MessageBox("Error6");
return;
}

int num=::SendMessage(wd1,TB_BUTTONCOUNT ,NULL,NULL);
int i;
unsigned long n;
TBBUTTON p,*pp;
CString x;
wchar_t name[256];

TBBUTTON *sp;

sp= (TBBUTTON *)0x20f00;
for(i=0;i<num;i++)
{
::SendMessage(wd1,TB_GETBUTTON,i,(LPARAM)sp);
pp=&p;
ReadProcessMemory(hd,sp,pp,sizeof(p),&n);
x.Format("%x %x %x %x %x %x",p.iBitmap,p.idCommand
,p.fsState,p.fsStyle, p.dwData, p.iString);
name[0]=0;
if (p.iString!=0xffffffff)
{
try
{
ReadProcessMemory(hd,(void *)p.iString,name,255,&n);
name[n]=0;
}
catch(...)
{
}
x+=" ";
x+=name;
m_list.AddString(x);
}


}

}
--------------结果---------------------------------
0 0 4 0 c9188 c91c0 虚拟 DAEMON 管理程序 V3.33
1 1 4 0 89340 c9218 Maxthon
2 3 4 0 d0350 103b20 本地连接
速度: 100.0 Mbps
发送: 1,152 包
收到: 836 包
3 4 4 0 d0318 944c8 音量
7 8 4 0 14d4f0 12ed10 天网防火墙个人版
8 9 4 0 1198f8 14dd70 VirusScan 控制台
9 a 4 0 15e598 15b0f8 VShield

jiaofeng 2005-04-01
  • 打赏
  • 举报
回复
感谢 legendhui(何离心之可同兮,吾将远逝以自疏)
可这不是我想要的
我想知道的是怎样知道托盘中有几个图标以及它们所对应的进程
legendhui 2005-04-01
  • 打赏
  • 举报
回复


这些都在我记事本里,没有连接了

哎,主要是想提供一些资料,说不定能从中得到些东西,看来。。。。。。
chen18s 2005-04-01
  • 打赏
  • 举报
回复
mark
billy145533 2005-04-01
  • 打赏
  • 举报
回复
呵呵,楼上说的是,又不是抢答,
huwei001982 2005-04-01
  • 打赏
  • 举报
回复
晕, 好大块的文章啊
翻页翻得好累啊, 可不可以只给个链接?

还有, 先理解清楚楼主的意思啊!
柯本 2005-03-31
  • 打赏
  • 举报
回复
楼上的,文章不错,可仍没有我想要的.
我想知道的只是怎样系统托盘中一共有几个图标,及它们所对应的进程ID或HANDLE
legendhui 2005-03-31
  • 打赏
  • 举报
回复

提醒一点,为使框架程序识别视图类,还要在MainFrm.cpp中加入如下两句:
#include "TrayDemoDoc.h"
#include "TrayDemoView.h"

Windows 95以及后来的Windows版本允许你将程序图标放入系统托盘。所谓系统托盘,通常指的是屏幕右下方显示时间,音量等图标的那个区域。这个区域主要用于显示状态信息或者当你运行的程序不可见时,允许你方便地访问程序的主要特性。这个区域还可以用于显示小程序的图标,以便用户容易访问主程序,或者在预定的时间加载主程序。

有些系统托盘图标可以变化用以指示程序状态,例如,浏览器的系统托盘图标当modem接收和发送数据时显示的是不同的图标。把鼠标移到托盘图标上停留一下常常会显示一个提示,根据程序的状态,它可能也会变化。在托盘图标上单击鼠标右键常常显示一个程序菜单,而双击鼠标左键常常可以启动主窗口或应用程序。

访问系统托盘的方法是通过Shell_NotifyIcon函数和NOTIFYICONDATA结构实现的。
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
UINT uTimeout; //Version 5.0
TCHAR szInfoTitle[64]; //Version 5.0
DWORD dwInfoFlags; //Version 5.0
} NOTIFYICONDATA, *PNOTIFYICONDATA;

为了要在系统托盘中显示图标,用NIM_ADD标志调用Shell_NotifyIcon函数。
#define ID_TASKBARICON 100
#define WM_ICONNOTIFY (WM_USER + 101)

NOTIFYICONDATA nid;

// 初始化系统托盘图标
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = ID_TASKBARICON;
nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;
nid.uCallbackMessage = WM_ICONNOTIFY;
nid.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_TRAY1), IMAGE_ICON, 16, 16, 0);
strcpy(nid.szTip, "My Tooltip Text");
Shell_NotifyIcon(NIM_ADD, &nid);

cbSize成员是结构的大小(使用它主要是为了支持将来这个结构大小增加)。
hWnd是窗口句柄。当图标发生某事件时(如单击、双击等),Windows将向窗口发送uCallbackMessage成员指定的消息。uID成员指定与图标关联的ID。它不是很重要,除非你需要显示并跟踪几个图标。
uFlag成员告诉Windows应该读取哪个成员。当添加一个图标时,应该包含这个结构的大多数成员。当更新图标时,如只是需要改变图标时,你只要设置相应
的标志就可以了。

hIcon成员是你想显示的图标。
最后,szTip成员是提示文本。设置好这些结构成员后,调用Shell_NotifyIcon函数。

当与图标关联的事件发生时,Windows将发送uCallbackMessage成员指定的消息。IParam包含发送的消息。当获得WM_LBUTTONDBLCLK消息时显示主窗口
或者启动主程序。当获得WM_RBUTTONUP消息时显示菜单。

注意:如果在系统托盘中单击鼠标右键,有时会有一个弹出式(上下文菜单)菜单显示/消失的怪现象,详细信息击解决办法请参阅微软知识库文章Q135788,也可以参考下列代码加以解决。
switch(nMsg) {
case WM_ICONNOTIFY:
switch(lParam) {
case WM_LBUTTONDBLCLK:
// Load main window here
break;
case WM_RBUTTONUP:
{
POINT point;
HMENU hMenu, hSubMenu;
// Get mouse position
GetCursorPos(&point);
// Popup context menu
hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MYMENU));
hSubMenu = GetSubMenu(hMenu, 0);
SetMenuDefaultItem(hSubMenu, IDM_DEFAULTCMD, FALSE);
SetForegroundWindow(hMainDlg); // Per KB Article Q135788
TrackPopupMenu(hSubMenu,
TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
point.x, point.y, 0, hWnd, NULL);
PostMessage(hMainDlg, WM_NULL, 0, 0); // Per KB Article Q135788
DestroyMenu(hMenu);
}
break;
default:
return FALSE;
}
}

不论什么时候,你都可以用 NIM_MODIFY 调用 Shell_NotifyIcon。程序终止之前,用 NIM_DELETE 调用 Shell_NotifyIcon从托盘中清除图标。

Shell_NotifyIcon(NIM_DELETE, &nid);

legendhui 2005-03-31
  • 打赏
  • 举报
回复

以下以在VC++6.0中具体实现的程序为例。该程序将拥有以下功能:程序被执行
后,首先显示一对话框表示程序开始执行,然后该对话框消失。接着程序图标
被加入到系统托盘中,可以看到,该图标将是一动画图标。当鼠标在该系统托
盘上右击时,将弹出一菜单。如图所示(略)。其第一项为缺省项命令,单击
将显示应用程序。为简化编程,该应用程序只是显示一应用程序主窗口。而单击
菜单第二项将关闭机器,单击菜单第三项将结束本程序。当并且当用户双击时,
CTrayIcon将执行菜单上的第一项:显示服务程序,这将击活(显示)TrayDemo
(正常情况下,它是隐藏的)。而要终止TrayDemo,你得选择结束本程序。当你
执行File Exit或关掉TrayDemo主窗口时,TrayDemo并没有真正的关掉,它只不过
隐藏起来了而已。TrayDemo 重载了Cmainframe::OnClose函数以执行该项功能。
首先在VC++6.0中生成用应用程序向导生成一单文档工程TrayDemo,然后在工程中
加入以上的CTrayIcon类。

要使用CTrayIcon类,你首先得实例化一个CTrayIcon类对象,TrayDemo在视图中
完成此项工作。以下是对应代码:

class CTrayDemoView : public CView {
protected: CTrayIcon m_trayIcon;
// my tray icon
.
.
.
};

当你实例化一个CTrayIcon类对象之后,你必须分配给其一个ID号。该ID号是此图
标在其生命周期内使用的唯一一个ID号,即使在以后你改变了实际显示的图标。此
ID号是当鼠标事件发生时你获得的ID。它可以不必是图标的资源ID;在TrayDemo
中,其值是IDR_TRAYICON,由CTrayDemoView构造函数所初始化。
CTrayDemoView::CTrayDemoView() :
m_trayIcon(IDR_TRAYICON){
.
.
.
}

要增加图标,可调用SetIcon重载函数之一
m_trayIcon.SetIcon(IDI_MYICON); //参数为资源ID
m_trayIcon.SetIcon("myicon"); //参数为资源名
m_trayIcon.SetIcon(hicon); //参数为HICON句柄
m_trayIcon.SetStandardIcon(IDI_WINLOGO);
//加入系统图标

除了SetIcon(UINT uID)函数需要一个同样拥有uID号的字符串资源作为提示字符串
以外,所有这些函数都有一个可选的指向提示字符串的LPCSTR参数。例如,在
TRAYTEST中有以下行:
// (In TrayDemoView.cpp)
m_trayIcon.SetIcon(IDI_RED);

该语句在增加图标的同时同样设定了提示字符串,因为TrayDemo有一个同样ID的字
符串:如果你想改变图标,只需再次调用其中的一个SetIcon函数,只不过需要不
同的ID或HICON。CTrayIcon类知道响应NIM_MODIFY消息而不是NIM_ADD消息。同样
的函数甚至可以去掉图标:
m_trayIcon.SetIcon(0);//removeicon

CtrayIcon类会将其解释为NIM_DELETE事件。这么多的代码和标志只用一个简单的
重载函数就予以完成,这是C++的伟大之处。
如果要显示动画图标,只需设置一定时器,然后在定时器的响应事件中调用
SetIcon成员函数就可以了。如:

int CTrayDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
m_timerID = this->SetTimer(99,200,NULL);

}

void CTrayDemoView::OnTimer(UINT nIDEvent)
{
uChangeIcon++;
if(uChangeIcon-IDI_RED>2)
uChangeIcon=IDI_RED;
m_trayIcon.SetIcon(uChangeIcon);
CView::OnTimer(nIDEvent);
}

在示例程序中,有3个图标,其ID为IDI_RED,IDI_YELLO,IDI_GREEN,且其ID值是相
连的,因而UINT型变量uChangeIcon用来依次轮换三个图标。这样程序执行以后,你
将会看到红、黄、绿三个交通指示灯依次闪烁。
那么怎样处理托盘通知呢?

要处理托盘通知,需要在你设定图标之前调用CTrayIcon::SetNotificationWnd函
数,当然你必须已经创建了窗口。最适当的地方是在OnCreate函数中,在TrayDemo
中也是这样做的。用ClassWizard在CtrayDemoView类中加入WM_CREATE消息响应函
数OnCreate(),并加入以下代码:

// Private message used for tray notifications
#define WM_MY_TRAY_NOTIFICATION WM_USER+0
int CTrayDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
.
.
.
m_trayIcon.SetNotificationWnd(this,WM_MY_TRAY_NOTIFICATION);
m_trayIcon.SetIcon(IDI_RED);
return 0;
}

然后进行消息注册(REGISTER),一旦注册以后,你就可以用正常的消息映射方式
处理托盘通知。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_MESSAGE(WM_MY_TRAY_NOTIFICATION,OnTrayNotification)
// (or ON_REGISTERED_MESSAGE)
END_MESSAGE_MAP()

当然不要忘了在TrayDemoView.h中加入以下语句:
afx_msg LRESULT OnTrayNotification(WPARAM wp, LPARAM lp);

当你的处理程序得到在托盘图标上的鼠标事件的控制以后,WPARAM参数是你在创建
CTrayIcon类时定义的ID;LPARAM是鼠标事件(如,WM_LBUTTONDOWN)。当捕获到
通知后你可以做任何你想做的事情;记得最后要调用
CTrayIcon::OnTrayNotification函数以完成一些缺省的处理。该虚函数完成前面
所提到的一些缸省的UI行为。特别的,它处理WM_LBUTTONDBLCLK和WM-RBUTTONUP事
件。CTrayIcon类寻找与图标拥有同样ID的菜单(如,IDR_TRAYICON)。如果拥有
该ID的菜单存在,CTrayIcon类将在用户右击图标的时候显示此菜单;而当用户双
击时,CTrayIcon将执行菜单上的第一个命令。

LRESULT CTrayDemoView::OnTrayNotification(WPARAM wp, LPARAM lp)
{
return m_trayIcon.OnTrayNotification(wp, lp);
}

只有两件事需要进一步解释。在显示菜单之前,CTrayIcon类使得第一项为缸省项,
因此它看起来是大写的。但怎样使得一个菜单项大写呢?使用函数
GSetMenuDefaultItem。
// Make first menu item the default (bold font)
::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);

这里的0便指定了第一个菜单项,TRUE表示通过位置而不是ID来确定菜单项。
对CTrayIcon::OnTrayNotification,我们关心的第二项是为了显示相关菜单,它干
了些什么?

::SetForegroundWindow(m_nid.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu, ...);

为了使TrackPopupMenu函数在托盘环境中工作正常,你必须首先在拥有该弹出菜单
的窗口中调用SetForegroundWindow函数。否则,当用户按下Esc键或在菜单以外单
击鼠标时该菜单将不会消失。正如你看到的那样,CTrayIcon类使得托盘图标的编
程很简单。为了使托盘菜单生效,在TrayDemo中所做的只是实现一个通知程序,在
该程序中调用了CTrayIcon::OnTrayNotification,对了别忘了还要提供一个与
CTrayIcon类拥有同样ID的菜单。TrayDemo程序中是在菜单编辑器内加入一ID为
IDR_TRAYICON的如下菜单:
然后,用ClassWizard在视图类中分别为三个菜单命令加入如下的响应函数:

void CTrayDemoView::OnDisplayProgram()
{
CWnd* pWnd;
pWnd=AfxGetApp()->m_pMainWnd;
pWnd->ShowWindow(SW_NORMAL);
pWnd->SetForegroundWindow();
}

void CTrayDemoView::OnCloseProgram()
{
m_bShutdown = TRUE; // really exit
CWnd* pWnd;
pWnd=AfxGetApp()->m_pMainWnd;
pWnd->SendMessage(WM_CLOSE);
}

void CTrayDemoView::OnShutoff()
{
ExitWindowsEx(EWX_SHUTDOWN,0);
}

其中,在OnShutoff函数中,ExitWindowsEx(EWX_SHUTDOWN,0)用来关闭计算机。限
于篇幅,这里不作详细介绍,读者可以查看MSDN来获得更详细的资料。
最后,还要重载Cmainframe::OnClose函数如下:

void CMainFrame::OnClose()
{
CTrayDemoView *pView =
(CTrayDemoView *)GetActiveView();
if (pView->m_bShutdown)
CFrameWnd::OnClose();
else
ShowWindow(SW_HIDE);
}
legendhui 2005-03-31
  • 打赏
  • 举报
回复
摘要
本文对如何将应用程序的图标加入到Windows的系统托盘中做了较为详细的介绍,
然后给出了一个C++类以方便的实现该功能,并在VC++6.0中给出了一个应用程序
实例来体现其具体实现过程。同时该应用程序实例还讲解了如何在托盘中实现动
画图标以及在程序中关闭计算机的技术。

关键词:系统托盘 动画图标

Windows98桌面的系统托盘位于任务栏的右侧,即Windows98桌面的右下方。它常
用来显示一些系统的状态。如:系统时间,音量控制以及其它的一些图标(依个
人机器安装的软件而不定),如下图为笔者的Windows98系统托盘。(图略)

常常能见到一些优秀的软件在运行后会将其应用程序图标加入到系统托盘中,如
金山词霸。如果能将自己编写的应用程序的图标也加入到系统托盘中,将会使你
的程序显得很有专业水准。

其实这并不困难,与系统托盘通信的函数只有一个:

Shell_NotifyIcon (UINT message, NOTIFYICONDATA &m_nid);

首先看一下该函数的两个参数。
第一个参数message可以取以下值:

NIM_ADD 向托盘中加入一个图标;
NIM_MODIFY 修改托盘中的图标
NIM_DELETE 从托盘中删除一个图标

第二个参数m_nid是NOTIFYICONDATA结构的一个引用。该结构的原型如下:
typedef struct _NOTIFYICONDATA
{
DWORD cbSize;// 结构的大小,必须在程序中给出
HWND hWnd;
//是你程序中将要接收托盘消息的窗口句柄
UINT uID;
// 应用程序中定义的托盘图标ID,此参数用作标识
UINT uFlags;
//设置属性,低三位有意义,0--7,如下:
//第一位//#define NIF_MESSAGE 0x1
// uCallbackMessage参数有效

//第二位//#define NIF_ICON 0x2 // hIcon参数有效
//第三位//#define NIF_TIP 0x4 // szTip参数有效

UINT uCallbackMessage;
// 自定义的消息ID值,一定不要与以有的消息ID相重。
HICON hIcon;
//显示在系统托盘上的Icon的句柄,可以为系统的 IDI_WINLOGO等
CHAR szTip[64]; // 用于图标显示的提示字符串
} NOTIFYICONDATA;

为了接收到来自托盘的通知消息你可以将uCallbackMessage设定为你所定义的消息
ID值,同时设定NIF_MESSAGE标志。这样当用户在你的托盘图标上移动或按下鼠标
时,Windows将发出消息:该消息的 messageID是你在uCallbackMessage中定义的
值;wParam是你定义的uID值;而lParam是鼠标事件(如WM_LBUTTONDOWN),这样你
的应用程序就能响应该事件了。
因此,为了将自己的应用程序加入到系统托盘中,首先得建立一处理托盘通知消息
的窗口对象,然后将窗口对象与你自己的托盘通知消息联系起来并建立相应的托盘
通知消息映射机制,以便你的窗口对象能处理相应的事件。

可以看到结构体NOTIFYICONDATA中,其成员变量hWnd,uID,uFlags均用于在窗口对
象与你自己的托盘通知消息之间建立联系,而成员变量uCallbackMessage则必须是
对应于你的窗口对象的托盘通知消息ID值。

于是要完成的工作有:

(1)建立一处理托盘通知消息的窗口对象;
(2)建立一结构体NOTIFYICONDATA变量,并给变量的相应域赋值以在托盘通知消
息与窗口对象之间建立联系;
(3)建立相应的托盘通知消息映射机制;
(4)调用Shell_NotifyIcon函数以在系统托盘中加入、修改或删除图标;
(5)当然别忘了在你的窗口对象中编写相应的事件响应函数。

因此,可以编写一C++类来实现以上功能以简化编程同时提高代码的可重用性。以
下为该类代码:

class CTrayIcon : public CCmdTarget {
protected:
DECLARE_DYNAMIC(CTrayIcon)
NOTIFYICONDATA m_nid;
// Shell_NotifyIcon 函数中的结构参数

public:
CTrayIcon(UINT uID);
~CTrayIcon();

// 通过调用该成员函数来接收托盘通知消息
void SetNotificationWnd(CWnd* pNotifyWnd,
UINT uCbMsg);

// SetIcon 函数用来在系统托盘中加入、改变及删除图标。
//要删除图标这样调用:SetIcon(0)
BOOL SetIcon(UINT uID);
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);
};

CTrayIcon::CTrayIcon(UINT uID)
{
//初始化NOTIFYICONDATA结构变量
memset(&m_nid, 0 , sizeof(m_nid));
m_nid.cbSize = sizeof(m_nid);
m_nid.uID = uID;
AfxLoadString(uID, m_nid.szTip, sizeof
(m_nid.szTip));
}

CTrayIcon::~CTrayIcon()
{
SetIcon(0); // 从系统托盘中删除图标
}

// 设定通知窗口,该窗口必须已被创建
void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
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)
{
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)
{
UINT msg;
m_nid.uFlags = 0;

// 设定图标
if (hicon) {
// 判断是要在系统托盘中增加还是要删除图标
msg = m_nid.hIcon ? NIM_MODIFY : NIM_ADD;
m_nid.hIcon = hicon;
m_nid.uFlags |= NIF_ICON;
} else { // 删除图标
if (m_nid.hIcon==NULL)
return TRUE; //已被删除
msg = NIM_DELETE;
}
if (lpTip)
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)
m_nid.uFlags |= NIF_MESSAGE;

BOOL bRet = Shell_NotifyIcon(msg, &m_nid);
if (msg==NIM_DELETE || !bRet)
m_nid.hIcon = NULL;
return bRet;
}
// 缺省事件处理程序,该程序处理鼠标右击及双击事件。
LRESULT CTrayIcon::OnTrayNotification(WPARAM wID,
LPARAM lEvent)
{
if (wID!=m_nid.uID ||
(lEvent!=WM_RBUTTONUP && lEvent!=WM_LBUTTONDBLCLK))
return 0;

// 使用与托盘图标拥有同样ID号的菜单作为右键弹出菜单
// 并将菜单上的第一项作为缺省命令使用,
// 缺省命令在WM_LBUTTONDBLCLK事件发生时被击发
//
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 // 双击事件: 执行菜单第一项
::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->
GetMenuItemID(0), 0);

return 1; // 表示事件已被处理
}
surstar 2005-03-31
  • 打赏
  • 举报
回复
MARK 关注
legendhui 2005-03-31
  • 打赏
  • 举报
回复
VC编程制作系统托盘程序
Windows操作系统中的某些程序运行时不显示运行窗口,只在任务栏上显示一个图标,表示程序正在运行,用户可以通过鼠标与应用程序交互,比如金山毒霸等应用程序,我们有时也需要编制一些仅在后台运行的类似程序,为了不干扰前台程序的运行界面和不显示不必要的窗口,应使程序运行时的主窗口不可见。同时将一个图标显示在任务栏右端静态通告区中并响应用户的鼠标动作。下面介绍Visual C++开发这类程序的设计方法。




一、隐藏程序的主窗口

首先,要使程序的主窗口不可见,并且不在任务栏上出现任务按钮,要做到这两点,需分别设置主边框窗口的风格和扩展风格:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
 cs.style =WS_POPUP;//使主窗口不可见
 cs.dwExStyle |=WS_EX_TOOLWINDOW;//不显示任务按钮
 return CFrameWnd::PreCreateWindow(cs);
}

二、将表示程序运行的图标加入任务栏

在主框架窗口的CMainFrame::OnCreate()函数中调用上述函数,就可以在任务条上显示图标这一步是利用系统API函数Shell_NotifyIcon()将一个图标显示在任务栏的通告区中。该函数的原型为:在调用该函数之前,需要确定其参数的取值。其中Shell_NotifyIcon()函数的第一个参数是一个预定义的消息,可以取如下值之一:NIM_ADD、NIM_DELETE或NIM_MODIFY,分别表示添加图标、删除图标或修改图标。另一个参数为指向NOTIFYICONDATA类型的指针。其原型为:

typedef struct _NOTIFYICONDATA {
 DWORD cbSize;
 HWND hWnd;
 UINT uID;
 UINT uFlags;
 UINT uCallbackMessage;
 HICON hIcon;
 charszTip[64]; }
NOTIFYICONDATA

在该结构的成员中,cbSize为该结构所占的字节数,hWnd为接受该图标所发出的消息的窗口的句柄(鼠标在任务栏上程序图标上动作时图标将发出消息,这个消息用户要自己定义),uID为被显示图标的ID,uFlags指明其余的几个成员(hIcon、uCallBackMessage和szTip)的值是否有效,uCallbackMessage为一个用户自定义的消息,当用户在该图标上作用一些鼠标动作时,图标将向应用程序的主框架窗口(hWnd成员中指定的窗口)发出该消息,。hIcon为将在任务栏上被显示图标的句柄,szTip鼠标停留在该图标上时显示的字符串。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 NOTIFYICONDATA tnd;
 tnd.cbSize=sizeof(NOTIFYICONDATA);
 tnd.hWnd=this->m_hWnd;
 tnd.uID=IDR_MAINFRAME;
 tnd.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
 tnd.uCallbackMessage=WM_MYMESSAGE;
 file://用户自定义的消息,即鼠标在任务栏上程序图标上动作时图标发送的消息
 tnd.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
 strcpy(tnd.szTip,"测试程序");//图标提示为"测试程序"
 Shell_NotifyIcon(NIM_ADD,&tnd);//向任务栏添加图标
}

三、用户与程序交互的实现

用户进行交互,也就是当用户在该图标上单击或双击鼠标左键或右键时要执行相应的操作,至少也要响应用户终止该程序的意愿。上面已经提到,当用户在图标上进行鼠标动作时,将向hWnd成员中指定的窗口发出自定义的消息,该消息为uCallbackMessage成员所指定的WM_MYESSAGE,取值为WM_USER+101(如何自定义消息,我就不多说了)。我们要实现任务就是在hWnd窗口中响应该自定义消息:

void CMainFrame::OnMYMESSAGE(WPARAM wParam,LPARAM lParam)
{
 UINT uID;//发出该消息的图标的ID
 UINT uMouseMsg;//鼠标动作
 POINT pt;
 uID=(UINT) wParam;
 uMouseMsg=(UINT) lParam;
 if(uMouseMsg==WM_RBUTTONDOWN)//如果是单击右键
 {
  switch(uID)
  {
  case IDR_MAINFRAME://如果是我们的图标
   GetCursorPos(&pt);//取得鼠标位置
   AfxGetApp( )-> m_pMainWnd->ShowWindow(SW_SHOWNORMAL);//显示程序窗口
   break;
   default:
  }
 }
 return;
}

四、程序结束时删除程序图标

当程序结束时,需要删去通告区中的图标,这时还应该调用Shell_NotifyIcon函数,只不过第一个参数是表示删除图标的NIM_DELETE了:

void CMainFrame::~CmainFrame()
{
 NOTIFYICONDATA tnid;
 tnid.cbSize=sizeof(NOTIFYICONDATA);
 tnid.hWnd=this->m_hWnd;
 tnid.uID=IDR_MAINFRAME;//保证删除的是我们的图标
 Shell_NotifyIcon(NIM_DELETE,&tnid);
}



以下是托盘程序的一个实例,应该对你有用的!!!



//初始化
WINSHELLAPI BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData);
WINSHELLAPI BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData);
#ifdef UNICODE
#define Shell_NotifyIcon Shell_NotifyIconW
#else
#define Shell_NotifyIcon Shell_NotifyIconA
#endif // !UNICODE

//建立托盘
BOOL CPageDlg::TaskBarAddIcon(HWND hwnd, UINT uID, HICON hicon, LPSTR lpszTip)
{
NOTIFYICONDATA d;
d.cbSize=sizeof(NOTIFYICONDATA);
d.hWnd=hwnd;
d.uID=uID;
d.uFlags=NIF_ICON | NIF_TIP | NIF_MESSAGE;
d.uCallbackMessage=MYWM_NOTIFYICON;
d.hIcon=hicon;

if(lpszTip)
lstrcpy(d.szTip,lpszTip);
else
d.szTip[0]='\0';
return Shell_NotifyIcon(NIM_ADD,&d);


}

//删除托盘
BOOL CPageDlg::TaskBarDeleteIcon(HWND hwnd, UINT uID)
{

NOTIFYICONDATA d;
d.cbSize=sizeof(NOTIFYICONDATA);
d.hWnd=hwnd;
d.uID=uID;

return Shell_NotifyIcon(NIM_DELETE,&d);
}


//改变托盘图标
BOOL CPageDlg::TaskBarModifyIcon(HWND hwnd, UINT uID, HICON hicon, LPSTR lpszTip)
{
NOTIFYICONDATA d;
d.cbSize=sizeof(NOTIFYICONDATA);
d.hWnd=hwnd;
d.uID=uID;
d.uFlags=NIF_ICON | NIF_TIP | NIF_MESSAGE;
d.uCallbackMessage=MYWM_NOTIFYICON;
d.hIcon=hicon;

if(lpszTip)
lstrcpy(d.szTip,lpszTip);
else
d.szTip[0]='\0';
return Shell_NotifyIcon(NIM_MODIFY,&d);

}
柯本 2005-03-31
  • 打赏
  • 举报
回复
前几天刚有人问过,可还没解决,我也想知道
http://community.csdn.net/Expert/topic/3883/3883888.xml?temp=.2073023

15,976

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧