VC实现程序最小化到系统托盘功能

 

     原文地址:《VC托盘编程》 

       以VC编程中,经常涉及到托盘操作,比如为我们的程序添加托盘图标,添加托盘左键响应或右键菜单,我们常用的QQ就是个托盘程序。

  好了下面我们来看下VC中托盘编程要注意些什么。首先我们要弄清楚一个结构体-NOTIFYICONDATA,这个结构体是托盘编程的关键。

typedef struct _NOTIFYICONDATA {
  DWORD cbSize;
  HWND hWnd;
  UINT uID;
  UINT uFlags;
  UINT uCallbackMessage;
  HICON hIcon;
  TCHAR szTip[64];
  DWORD dwState;
  DWORD dwStateMask;
  TCHAR szInfo[256];
  union {
  UINT uTimeout;
  UINT uVersion;
  };
  TCHAR szInfoTitle[64];
  DWORD dwInfoFlags;
  GUID guidItem;
  } 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风格的图标消息接口。请参考Shell_NotifyIcon函数的说明获取更多信息。只有当使用Shell_NotifyIcon函数发送NIM_SETVERSION消息时,此成员才有作用。可选的值如下:
  0  使用Windows 95风格。针对Windows 2000版本之前的windows设计的软件请使用此值。
  NOTIFYICON_VERSION  使用Windows 2000风格。 针对Windows 2000版本以及以后版本的windows设计的软件请使用此值。
szInfoTitle Version 5.0. 指向一个以\0结束的字符串的指针。字符串的内容为气球提示的标题。此标题出现在气球提示框的上部,最多含有63个字符。
dwInfoFlags Version 5.0. 设置此成员用来给气球提示框增加一个图标。增加的图标出现在气球提示标题的左侧,注意如果szInfoTitle成员设为空字符串,则图标也不会显示。可选值如下:
  NIIF_ERROR  错误图标。
  NIIF_INFO   信息图标。

  NIIF_NONE   没有图标。
  NIIF_USER   使用用户使用hIcon成员指明的图标,要求Windows XP Service Pack 2 (SP2)或以后系统。
  NIIF_WARNING  警告图标。
  NIIF_ICON_MASK  Version 6.0. 保留。
  NIIF_NOSOUND    Version 6.0. 禁止播放相应声音。
guidItem  Version 6.0. 保留。

 

  成员比较多,所以说明也挺长,如我上面所说的,搞清楚前面十来个成员代表的含意就行了,后面的不常用。好了,我们看下托盘编程的一般步骤,首先在我们的测试工程(MFC 应用程序)里的对话框内声明一个全局NOTIFYICONDATA结构体变量:

  NOTIFYICONDATA m_SkyNID;

然后在OnInitDialog函数中初始化:

  m_SkyNID.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
  m_SkyNID.hWnd = this->m_hWnd;
  m_SkyNID.uID = IDR_SKYNID_MENU;
  m_SkyNID.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP ;
  m_SkyNID.dwState = NIS_SHAREDICON;
  m_SkyNID.uCallbackMessage = WM_TASKMANAGE;
  m_SkyNID.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
  _tcscpy(m_SkyNID.szTip, _T("系统状态-未登录"));
  Shell_NotifyIcon(NIM_ADD, &m_SkyNID);

***************************上面的代码在VC6.0下面编译有错误,可以参照《VC6.0托盘编程》上面有解决之道****************************

搞清楚结构体各成员所代表的含意,上面的初始化代码应该不难看懂,m_SkyNID.uID我设置为菜单资源的ID,其实这个可以自定义,只要唯一就行了。最后一句用windows API Shell_NotifyIcon将初始化的结构体变量所代表的托盘图标添加到系统托盘中,这样你的桌面右下解就会出现你的应用程序托盘图标。当然,光这样在程序退出的时候这个图标不会自动删除,要在程序退出时添加处理代码,我是这样做的,在OnDestroy函数中添加代码:

  Shell_NotifyIcon(NIM_DELETE, &m_SkyNID);

至此你的程序能在启动和退出的时候自动在系统托盘中创建和删除图标,接下来我们为这个托盘添加右键菜单。在资源中添加菜单,我这里菜单资源ID是IDR_SKYNID_MENU。

由于在初始化代码中m_SkyNID.uCallbackMessage = WM_TASKMANAGE设置了托盘图标区域发生鼠标事件或者使用键盘选择或激活图标时,hWnd标示的窗口会收到WM_TASKMANAGE消息(这是个自定义消息#define WM_TASKMANAGE WM_USER+100),下面我们就在对应窗口中响应这个消息,在消息映射宏中添加消息映射
  ON_MESSAGE(WM_TASKMANAGE,OnTaskManage)

并添加消息响应函数OnTaskManage

  LRESULT CSkyTestDlg::OnTaskManage(WPARAM wParam, LPARAM lParam)
  {
    if(wParam != IDR_SKYNID_MENU)
      return 1;

    switch(lParam)
    {
      case WM_RBUTTONUP:
      {
        CMenu * pTaskMenu, taskMenu;
        CPoint pMousePosition;
        taskMenu.LoadMenu(IDR_SKYNID_MENU);
        pTaskMenu = taskMenu.GetSubMenu(0);

              /******为了简单测试,也可以把pTaskMenu加载成系统菜单pTaskMenu=GetSystemMenu(false)**********/
        GetCursorPos(&pMousePosition);
        SetForegroundWindow();
        pTaskMenu->TrackPopupMenu(TPM_RIGHTALIGN,pMousePosition.x,pMousePosition.y,this);
        break;
      }
      case WM_LBUTTONDOWN:
      {
        break;
      }
    }
    return 0;

  }

/************************

上面代码涉及到的IDR_SKYNID_MENU菜单,可以如下面实行编辑:

记事本打开资源文件 **.rc,找到里面的代表菜单资源的地方编辑菜单如下:

IDR_SKYNID_MENU MENU DISCARDABLE 

BEGIN

    POPUP "MyMenu"

    BEGIN

        MENUITEM "关于",                        ID_ABOUT

        MENUITEM "还原",                        ID_RESET

        MENUITEM "退出",                        ID_EXIT

    END

END

也是就一个弹出式菜单

*******************************/


简单对上面代码的作一下解释先定义CMenu变量taskMenu并LoadMenu资源ID为IDR_SKYNID_MENU的菜单,再用CMenu指针变量得到加载的菜单指针,并利用TrackPopupMenu函数将菜单显示到指定的坐标。SetForegroundWindow()使用这个函数是为了将菜单设为前端窗口,解决鼠标离开菜单单击或双击菜单不会自动消失的问题。最后,对每个右键菜单项添加事件响应处理代码,右键菜单项->Add Event Handler系统自动添加三处代码,这个就不赘述了,以上面菜单的退出程序子项为例,看代码

  afx_msg void OnSkyTestExit();// 第一处 头文件的响应函数声明

  ON_COMMAND(ID_SKYTEST_EXIT, &CSkyTestDlg::OnSkyTestExit)  //第二处 cpp文件消息映射宏 

  void CSkyTestDlg::OnSkyTestExit()   // 第三处 cpp文件 消息响应函数
  {
    if(IsWindow(m_SkyLoginDlg.m_hWnd))
    {
      m_SkyLoginDlg.SendMessage(WM_CLOSE);
    }
    EndDialog(TRUE);
  }

至此我们的托盘右键菜单已经能正常工作了,文章比较简单,欢迎各位跟贴交流。

***************************************************************************************************************************************************

下面给出用到的函数说明!

***************************************************************************************************************************************************

一、Shell_NotifyIcon 函数说明  函数格式

  BOOL Shell_NotifyIcon( DWORD dwMessage,PNOTIFYICONDATA lpdata);

参数说明
  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。
  如果dwMessage参数设为NIM_SETVERSION,则如果版本设置成功返回TRUE,如果设置的版本不支持返回FALSE。

***************************************************************************************************************************************************

再来一份简短的介绍,步骤一样,熟能生巧!

***************************************************************************************************************************************************

原文地址:《VC++MFC对话框应用程序实现程序最小化到系统托盘》

   第一步:定义一个 NOTIFYICONDATA 结构变量 nid。NOTIFYICONDATA结构体定义可以在MSDN中查找,就不做多做介绍。

    第二步:设置NOTIFYICONDATA变量属性:
    nid.cbSize =sizeof(NOTIFYICONDATA);
    nid.hWnd =m_hWnd;
    nid.uID = 0;
    nid.hIcon =m_hIcon;
    nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP|NIF_INFO;
    nid.uCallbackMessage =WM_NOTIFYICON;//#define WM_NOTIFYICON (WM_USER +101) 自己定义
    wcscpy(nid.szTip, L"这是标签");
    wcscpy(nid.szInfo, L"这是气泡提示消息");
    wcscpy(nid.szInfoTitle, L"这是气泡提示标题");
    nid.dwInfoFlags = NIIF_INFO;
    nid.uTimeout = 1000;

   第三步:将图标显示到系统托盘,只需调用一个API即可完成.
   Shell_NotifyIcon (NIM_ADD,&nid);

   第四步:图标添加完成了,我们通常需要让它响应用户的各种操作。如双击显示窗体,右键弹出上下文菜单等.
首先,我们定义一个回调函数
LRESULT OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
    switch(lParam)//根据lParam判断相对应的事件
    {
    case WM_LBUTTONDBLCLK://如果左键双击托盘图标,则显示窗体
        ShowWindow (SW_SHOWNORMAL );
        SetForegroundWindow();
        break;
    case WM_RBUTTONUP://如果右键菜单弹起,则弹出菜单
        CPoint pos;
        GetCursorPos(&pos);
        if(pMenuContext != NULL)
        {
            SetForegroundWindow();//加这句是为了鼠标点击其他地方时,弹出的菜单能够消失
        pMenuContext->TrackPopupMenu(TPM_RIGHTBUTTON|TPM_RIGHTALIGN,pos.x+1,pos.y+1,this);
        }
        break;
    }
    return 0;
}

然后在BEGIN_MESSAGE_MAP 与END_MESSAGE_MAP 之间添加
ON_MESSAGE (WM_NOTIFYICON ,&OnNotifyIcon)
进行消息映射就完成了

   我们已经完成了对程序添加托盘图标,而且可以通过图标对应用程序进行一定的操作,但是当程序退出的时候,系统托盘图标并不会自动删除,所以我们还需要在 OnClose() 中调用::Shell_NotifyIcon (NIM_DELETE ,&nid);删除托盘图标.

你可能感兴趣的:(编程,windows,shell,delete,任务,menu)