为程序创建任务栏右下角托盘图标

OrdinAry

发布日期: 2018-12-20 12:32:31 浏览量: 1261
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

背景

我们用过很多应用程序,无论是功能复杂的大程序还是功能单一的小程序,大都会在计算机右下角显示一个图盘图标,然后我们可以直接把窗口关掉,通过点击托盘图标来控制程序的执行操作或显示窗口。使得程序使用起来比较便捷,不用显示程序主窗口就能完成一些操作,增强了程序的用户体验。

本文要讲解的正是为程序添加这样的一个右下角托盘,并实现显示程序主窗口、退出程序等功能。本文分别在MFC程序和Windows应用程序中进行演示,其实,原理步骤都是相同的,只是为了方便大家理解,所以MFC程序和Windows程序都各写一个来演示。

现在,我就把程序实现的过程整理成文档,分享给大家。

函数介绍

Shell_NotifyIcon 函数

主要用于向任务栏的状态栏发送一个消息。

函数声明

  1. BOOL Shell_NotifyIcon(
  2. DWORD dwMessage,
  3. PNOTIFYICONDATA lpdata
  4. );

参数

  • dwMessage为输入参数,传递发送的消息,表明要执行的操作。可选的值如下:
    NIM_ADD
    向托盘区域添加一个图标。此时第二个参数lpdata指向的NOTIFYICONDATA结构体中的hWnd和uID成员用来标示这个图标,以便以后再次使用Shell_NotifyIcon对此图标操作。
    NIM_DELETE
    删除托盘区域的一个图标。此时第二个参数lpdata指向的NOTIFYICONDATA结构体中的hWnd和uID成员用来标示需要被删除的这个图标。
    NIM_MODIFY
    修改托盘区域的一个图标。此时第二个参数lpdata指向的NOTIFYICONDATA结构体中的hWnd和uID成员用来标示需要被修改的这个图标。
    NIM_SETFOCUS
    Version 5.0. 设置焦点。比如当用户操作托盘图标弹出菜单,而有按下ESC键将菜单消除后,程序应该使用此消息来将焦点设置到托盘图标上。
    NIM_SETVERSION
    Version 5.0. 设置任务栏按照第二个参数lpdata指向的NOTIFYICONDATA结构体中的uVersion成员指定的版本号来工作。此消息可以允许用户设置是否使用基于Windows2000的version 5.0的风格。uVersion的缺省值为0,默认指明了使用原始Windows 95图标消息风格。具体这两者的区别请参考msdn中的Shell_NotifyIcon函数说明的Remarks。
  • lpdata为输入参数,是指向NOTIFYICONDATA结构体的指针,结构体内容用来配合第一个参数wMessage进行图标操作。

返回值

  • 如果图标操作成功返回TRUE,否则返回FALSE。

NOTIFYICONDATA 结构体

结构体声明

  1. typedef struct _NOTIFYICONDATA {
  2. DWORD cbSize;
  3. HWND hWnd;
  4. UINT uID;
  5. UINT uFlags;
  6. UINT uCallbackMessage;
  7. HICON hIcon;
  8. TCHAR szTip[64];
  9. DWORD dwState;
  10. DWORD dwStateMask;
  11. TCHAR szInfo[256];
  12. union {
  13. UINT uTimeout;
  14. UINT uVersion;
  15. };
  16. TCHAR szInfoTitle[64];
  17. DWORD dwInfoFlags;
  18. GUID guidItem;
  19. } NOTIFYICONDATA, *PNOTIFYICONDATA;

成员

  • cbSize:结构体的大小,以字节为单位。
  • hWnd:窗口的句柄。窗口用来接收与托盘图标相关的消息。Shell_NotifyIcon函数调用时,hWnd和uID成员用来标示具体要操作的图标。
  • uID:应用程序定义的任务栏图标的标识符。Shell_NotifyIcon函数调用时,hWnd和uID成员用来标示具体要操作的图标。通过将多次调用,你可以使用不同的uID将多个图标关联到一个窗口。hWnd。
  • uFlags:此成员表明具体哪些其他成员为合法数据(即哪些成员起作用)。此成员可以为以下值的组合:
    NIF_ICON:hIcon成员起作用。
    NIF_MESSAGE:uCallbackMessage成员起作用。
    NIF_TIP:szTip成员起作用。
    NIF_STATE:dwState和dwStateMask成员起作用。
    NIF_INFO:使用气球提示代替普通的工具提示框。szInfo, uTimeout, szInfoTitle和dwInfoFlags成员起作用。
    NIF_GUID:保留。
  • uCallbackMessage:应用程序定义的消息标示。当托盘图标区域发生鼠标事件或者使用键盘选择或激活图标时,系统将使用此标示向由hWnd成员标示的窗口发送消息。消息响应函数的wParam参数标示了消息事件发生的任务栏图标,lParam参数根据事件的不同,包含了鼠标或键盘的具体消息,例如当鼠标指针移过托盘图标时,lParam将为WM_MOUSEMOVE。
  • hIcon:增加、修改或删除的图标的句柄。注意,windows不同版本对于图标有不同要求。Windows XP可支持32位。
  • szTip:指向一个以\0结束的字符串的指针。字符串的内容为标准工具提示的信息。包含最后的\0字符,szTip最多含有64个字符。对于Version 5.0 和以后版本,szTip最多含有128个字符(包含最后的\0字符)。
  • dwState:Version 5.0,图标的状态,有两个可选值,如下:
    NIS_HIDDEN:图标隐藏
    NIS_SHAREDICON:图标共享
  • dwStateMask:Version 5.0. 指明dwState成员的那些位可以被设置或者访问。比如设置此成员为NIS_HIDDEN,将导致只有hidden状态可以被获取。
  • szInfo:Version 5.0. 指向一个以\0结束的字符串的指针。字符串的内容为气球提示内容。最多含有255个字符。如果要移除已经存在的气球提示信息,设置uFlags成员为NIF_INFO,同时将szInfo设为空。
  • uTimeout:和uVersion成员为联合体。uTimeout表示气球提示超时的时间,单位为毫秒,此时间后气球提示将消失。系统默认气球提示的超时时间最小值为10秒,最大值为30秒。如果设置的uTimeout的值小于10将设置最小值,如果大于30将设置最大值。将超时时间分为最大最小两种,是因为解决不同图标的气球提示同时弹出的问题,详细内容请参考MSDN中NOTIFYICONDATA结构体说明的remarks。
  • uVersion:Version 5.0. 和uTimeout成员为联合体。用来设置使用Windows 95 还是 Windows 2000风格的图标消息接口。
  • szInfoTitle:Version 5.0. 指向一个以\0结束的字符串的指针。字符串的内容为气球提示的标题。此标题出现在气球提示框的上部,最多含有63个字符。
  • dwInfoFlags:Version 5.0. 设置此成员用来给气球提示框增加一个图标。增加的图标出现在气球提示标题的左侧,注意如果szInfoTitle成员设为空字符串,则图标也不会显示。
  • guidItem:Version 6.0. 保留。

实现过程

无论是对MFC程序还是Windows应用程序,实现的步骤和原理都是一样的。

1. 设置 NOTIFYICONDATA

首先,我们需要对托盘图标结构体 NOTIFYICONDATA 进行设置,设置的内容,就是我们想要托盘图标显示的内容。我们主要是对 NOTIFYICONDATA 的cbSize、hWnd、uID、uFlags、uCallbackMessage、hIcon以及szTip成员进行设置。

其中,cbSize表示 NOTIFYICONDATA 结构体的大小;hWnd表示和托盘图标相关联的程序窗体的句柄,这个值需要被指定,因为窗口用来接收与托盘图标相关的消息;uID表示应用程序定义的任务栏图标的标识符,可以使用不同的uID将多个图标关联到一个窗口;uFlags表示哪些结构体成员起作用,NIF_ICON则hIcon成员起作用,NIF_MESSAGE则uCallbackMessage成员起作用,NIF_TIP则szTip成员起作用,所以下面我们还需要对hIcon、uCallbackMessage以及szTip成员赋值;hIcon表示托盘显示的图标句柄;uCallbackMessage表示托盘的消息类型;szTip表示托盘的提示信息。

  1. NOTIFYICONDATA notifyIconData = {0};
  2. ::RtlZeroMemory(&notifyIconData, sizeof(notifyIconData));
  3. notifyIconData.cbSize = sizeof(NOTIFYICONDATA);
  4. notifyIconData.hWnd = hWnd;
  5. notifyIconData.uID = IDI_ICON1;
  6. notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  7. notifyIconData.uCallbackMessage = WM_MYTASK;
  8. notifyIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), (LPCSTR)IDI_ICON1);
  9. ::lstrcpy(notifyIconData.szTip, "This Is A Notify Tip.");

2. 增加托盘显示

然后,我们开始调用 Shell_NotifyIcon 函数按照上面 NOTIFYICONDATA 设置新增托盘。其中,NIM_ADD表示向托盘区域添加一个图标。notifyIconData就是上面设置的 NOTIFYICONDATA 结构体变量。

  1. // 增加托盘
  2. ::Shell_NotifyIcon(NIM_ADD, &notifyIconData);

3. 托盘消息处理函数

经过上面两步操作,我们就可以成功为程序在窗口右下角增加一个托盘图标显示。但是,我们还需要让托盘图标对一些操作做出响应,而不是只是显示而已。例如,我们鼠标右击托盘图标的时候,显示菜单栏。

根据上面对 NOTIFYICONDATA 结构体成员 uCallbackMessage 介绍中知道 ,当托盘图标区域发生鼠标事件或者使用键盘选择或激活图标时,系统将使用此标示向由hWnd成员标示的窗口发送消息。消息响应函数的wParam参数标示了消息事件发生的任务栏图标,lParam参数根据事件的不同,包含了鼠标或键盘的具体消息,例如当鼠标指针移过托盘图标时,lParam将为WM_MOUSEMOVE。所以,我们要响应鼠标右键的操作,需要判断 lParam 是否为 WM_RBUTTONUP ,若是,弹出菜单,否则忽略操作。

  1. switch (lParam)
  2. {
  3. // 鼠标右键弹起时
  4. case WM_RBUTTONUP:
  5. {
  6. // 弹出菜单
  7. PopupMyMenu();
  8. break;
  9. }
  10. default:
  11. break;
  12. }

就这样,菜单弹出来后,我们直接响应菜单选项的消息响应函数,在对应的选项里执行相应的操作就好。

为程序添加托盘显示的原理就是上面 3 个步骤,剩下来的就是编码实现了。

编码实现

Windows应用程序实现

首先我们把对NOTIFYICONDATA结构体的设置以及添加托盘图标的操作代码都放在程序初始化WM_INITDIALOG操作里。

然后,我们就开始在窗口消息过程函数中添加托盘消息类型WM_MYTASK,并对实现托盘消息处理响应函数。这样,在右击托盘的时候,就可以显示菜单栏了。

窗口消息过程函数

  1. BOOL CALLBACK ProgMainDlg(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
  2. {
  3. if (WM_INITDIALOG == uiMsg)
  4. {
  5. // 设置托盘显示
  6. SetNotifyIcon(hWnd);
  7. }
  8. else if (WM_CLOSE == uiMsg)
  9. {
  10. // 隐藏窗口
  11. ::ShowWindow(hWnd, SW_HIDE);
  12. }
  13. else if (WM_MYTASK == uiMsg)
  14. {
  15. // 处理操作托盘的消息
  16. OnTaskMsg(hWnd, wParam, lParam);
  17. }
  18. else if (WM_COMMAND == uiMsg)
  19. {
  20. if (ID_EXIT == wParam)
  21. {
  22. // 托盘菜单 退出
  23. ::EndDialog(hWnd, NULL);
  24. }
  25. else if (ID_SHOW == wParam)
  26. {
  27. // 托盘菜单 显示主窗口
  28. ::ShowWindow(hWnd, SW_SHOW);
  29. }
  30. }
  31. return FALSE;
  32. }

设置托盘显示

  1. // 设置托盘显示
  2. void SetNotifyIcon(HWND hWnd)
  3. {
  4. NOTIFYICONDATA notifyIconData = {0};
  5. ::RtlZeroMemory(&notifyIconData, sizeof(notifyIconData));
  6. notifyIconData.cbSize = sizeof(NOTIFYICONDATA);
  7. notifyIconData.hWnd = hWnd;
  8. notifyIconData.uID = IDI_ICON1;
  9. notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  10. notifyIconData.uCallbackMessage = WM_MYTASK;
  11. notifyIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), (LPCSTR)IDI_ICON1);
  12. ::lstrcpy(notifyIconData.szTip, "This Is A Notify Tip.");
  13. // 增加托盘
  14. ::Shell_NotifyIcon(NIM_ADD, &notifyIconData);
  15. }

托盘消息处理函数

  1. // 处理操作托盘的消息
  2. LRESULT OnTaskMsg(HWND hWnd, WPARAM wParam, LPARAM lParam)
  3. {
  4. switch (lParam)
  5. {
  6. // 鼠标右键弹起时,弹出菜单
  7. case WM_RBUTTONUP:
  8. {
  9. // 弹出菜单
  10. PopupMyMenu(hWnd);
  11. break;
  12. }
  13. default:
  14. break;
  15. }
  16. return 0;
  17. }

弹出菜单栏

  1. // 弹出菜单栏
  2. void PopupMyMenu(HWND hWnd)
  3. {
  4. POINT p;
  5. ::GetCursorPos(&p);
  6. HMENU hMenu = ::LoadMenu(::GetModuleHandle(NULL), (LPCSTR)IDR_MENU1);
  7. HMENU hSubMenu = ::GetSubMenu(hMenu, 0);
  8. ::TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, 0, hWnd, NULL);
  9. ::DestroyMenu(hSubMenu);
  10. }

MFC程序实现

和上面的Windows应用程序一样,首先我们把对NOTIFYICONDATA结构体的设置以及添加托盘图标的操作代码都放在程序初始化,对于MFC来说,初始化消息响应函数是主对话框类的OnInitDialog函数。我们把托盘设置这部分代码,放在函数OnInitDialog中即可。

然后,我们开始对为自定义消息类型 WM_MYTASK 创建自定义消息响应函数 OnTaskMsg。创建自定义消息响应函数操作如下:

  • 声明消息类型 WM_MYTASK。

    1. #define WM_MYTASK (WM_USER + 100)
  • 声明消息响应函数,函数名称可以任意,但是返回值类型和参数类型是固定的。

    1. // 托盘消息处理函数
    2. LRESULT OnTaskMsg(WPARAM wParam, LPARAM lParam);
  • 在主对话框的窗口消息响应列表中,为上述自定义的消息类型和消息响应函数进行关联。

    1. BEGIN_MESSAGE_MAP(CNotifyIcon_MFC_TestDlg, CDialogEx)
    2. …(省略)
    3. ON_MESSAGE(WM_MYTASK, OnTaskMsg)
    4. END_MESSAGE_MAP()

这样,我们就可以直接定义响应函数OnTaskMsg就可以了。

接下来的操作和代码,基本上和Windows应用程序中的代码是一样的,所以,就给出代码了。若有什么问题,可以直接参考本文对应的程序代码即可。

程序测试

我们直接运行程序,便可以看到窗口右下角有托盘图标显示,然后我们鼠标右击图标,便成功弹出菜单栏,点击菜单栏选项,成功实现相应的操作。

总结

这个程序原理上不是很复杂,但是实现上面,由于Windows应用程序和MFC程序框架上不同,所以,要注意它们的区别,特别是MFC的自定义消息响应函数。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download NotifyIcon_Test.7z ( 2.59mb, 8次下载 )

发送私信

有没有那样一种永远,永远永远不改变

12
文章数
32
评论数
最近文章
eject