两种方法实现托盘功能

1为了实现托盘功能,我们可以使用消息机制来进行实现

我们需要使用到windows的一个API函数:BOOL Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA lpdata);其中dwMessage可以取以下值:

NIM_ADD 向托盘中加入一个图标

NIM_MODIFY 修改托盘中的图标

NIM_DELETE 从托盘中删除一个图标

参数pnid是NOTIFYICONDATA结构的一个引用。该结构的原型如下:

typedef struct _NOTIFYICONDATA {
    DWORD cbSize; // 结构的大小,必须在程序中给出
    HWND hWnd;    // 程序中将要接收托盘消息的窗口句柄
    UINT uID;     // 应用程序中定义的托盘图标ID,此参数用作标识
    UINT uFlags;  //设置属性 标记下边3个参数是否有效
    UINT uCallbackMessage;// 自定义的消息ID值
    HICON hIcon;//显示在系统托盘上的Icon的句柄
    #if (_WIN32_IE < 0x0500)
        TCHAR szTip[64;// 用于图标显示的提示字符串
    #else
        TCHAR szTip[128];
    #endif
    #if (_WIN32_IE >= 0x0500)
        DWORD dwState;
        DWORD dwStateMask;
        TCHAR szInfo[256];
        union {
            UINT  uTimeout;
            UINT  uVersion;
        } DUMMYUNIONNAME;
        TCHAR szInfoTitle[64];
        DWORD dwInfoFlags;
    #endif
    #if (_WIN32_IE >= 0x600)
        GUID guidItem;
    #endif
} NOTIFYICONDATA, *PNOTIFYICONDATA; 

(1)我们可以自定义一个托盘消息WM_TRAY和NOTIFYICONDATA 变量,在XXDlg.h中添加

define WM_TRAY WM_USER+1

NOTIFYICONDATA nid;

(2)在XXDlg.h中添加消息的处理函数

afx_msg LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam);

(3)在XXDlg.cpp中添加消息映射

ON_MESSAGE(WM_TRAY,OnTrayNotify)

(4)编写NOTIFYICONDATA nid初始化函数

void CTrayDlg::InitTray()
{
    nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
     nid.hWnd=this->m_hWnd;
     nid.uID=IDR_MAINFRAME;
    nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP ;
     nid.uCallbackMessage=WM_TRAY;//自定义的消息名称
     nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
    strcpy(nid.szTip,_T("程序名称")); //信息提示条
    Shell_NotifyIcon(NIM_ADD,&nid); //在托盘区添加图标
}

(5)在OnInitDialog()函数中调用InitTray()函数

(6)编写 LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam)函数实现代码

//wParam处理图标信息,lParam处理鼠标信息
LRESULT CTrayDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam)
{
    if(wParam!=IDR_MAINFRAME)
        return 1;
    switch(lParam)
    {
    case WM_RBUTTONUP://右键起来时弹出快捷菜单,这里只有一个“关闭”
        {
            LPPOINT lpoint=new tagPOINT;
            GetCursorPos(lpoint);//得到鼠标位置
            CMenu menu;
            menu.CreatePopupMenu();//声明一个弹出式菜单
            //增加菜单项“关闭”,点击则发送消息WM_DESTROY给主窗口(已隐藏),将程序结束。
            menu.AppendMenu(MF_STRING,WM_DESTROY,"关闭"); //确定弹出式菜单的位置
            SetForegroundWindow(); // 防止未选菜单不消失
            menu.TrackPopupMenu(TPM_LEFTALIGN,lpoint->x,lpoint->y,this); //资源回收
            HMENU hmenu=menu.Detach();
            menu.DestroyMenu();
            delete lpoint;
        }
        break;
    case WM_LBUTTONDBLCLK://双击左键的处理
        {
            //窗口前端显示
            SetForegroundWindow();
            ShowWindow(SW_SHOWNORMAL);
        }
        break;
    default: break;
    }
    return 0;
}

(7)添加WM_SIZE消息处理函数

void CTrayDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);

    // TODO: Add your message handler code here
    if (nType==SIZE_MINIMIZED)
    {
        ShowWindow(SW_HIDE);
    }
}

(8)添加WM_CLOSE消息处理函数

void CTrayDlg::OnClose()
{
    // TODO: Add your message handler code here and/or call default
     Shell_NotifyIcon(NIM_DELETE,&nid); //删除图标
    CDialog::OnClose();
}

2 直接对WindowProc函数进行重载,它是一个虚函数

LRESULT CTrayDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class
    switch(message) //判断消息类型
    {
    case WM_TRAY:
        //如果是用户定义的消息
        if(lParam==WM_LBUTTONDBLCLK)  

        {
            //鼠标双击时主窗口出现
            if(AfxGetApp()->m_pMainWnd->IsWindowVisible()) //判断窗口当前状态
            {
                AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE); //隐藏窗口
            }
            else
            {
                AfxGetApp()->m_pMainWnd->ShowWindow(SW_SHOW); //显示窗口
            }  

        }
        else if(lParam==WM_RBUTTONDOWN)
        { //鼠标右键单击弹出选单
            LPPOINT lpoint=new tagPOINT;
            GetCursorPos(lpoint);//得到鼠标位置
            CMenu menu;
            menu.CreatePopupMenu();//声明一个弹出式菜单
            //增加菜单项“关闭”,点击则发送消息WM_DESTROY给主窗口(已隐藏),将程序结束。
            menu.AppendMenu(MF_STRING,WM_DESTROY,"关闭"); //确定弹出式菜单的位置
            SetForegroundWindow(); // 防止未选菜单不消失
            menu.TrackPopupMenu(TPM_LEFTALIGN,lpoint->x,lpoint->y,this); //资源回收
            HMENU hmenu=menu.Detach();
            menu.DestroyMenu();
            delete lpoint;
        }
        break;
    case WM_SYSCOMMAND:
        //如果是系统消息
        if(wParam==SC_MINIMIZE)
        {
            //接收到最小化消息时主窗口隐藏
            AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE);
            return 0;
        }
        if(wParam==SC_CLOSE)
        {
            Shell_NotifyIcon(NIM_DELETE,&nid); //关闭时删除系统托盘图标
        }
        break;
    }
    return CDialog::WindowProc(message, wParam, lParam);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-16 06:15:47

两种方法实现托盘功能的相关文章

android 实现分享功能两种方法

当我想做一个智能的记事本的时候,我就在尝试自己写一组分享功能.后来才知道,原来每个社交软件中都有自己的分享接口. 这就大大减少了我们的代码量了. 第一种方法:特点--简单 package com.example.share; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuI

zabbix使用自己编写脚本模板和zabbix自带模板两种方法添加对指定进程和端口的监控

zabbix使用自己编写脚本模板和zabbix自带模板两种方法添加对指定进程和端口的监控 1.自带监控模板进行os的监控 进入/usr/local/zabbix/etc/zabbix_agentd.conf 配置文件修改 LogRemoteCommands=1     ###开启脚本功能 Server=192.168.5.129     ##修改zabbix指向的服务器: 重启zabbix_agentd.zabbix_server服务 在配置-->主机-->添加主机--> 配置主机信息主

在ArcEngine下实现图层属性过滤的两种方法

转自chanyinhelv原文 在ArcEngine下实现图层属性过滤的两种方法 有时候,我们要对图层上的地物进行有选择性的显示,以此来满足实际的功能要求. 按下面介绍的方法可轻松实现图层属性过滤显示: 1.当图层已经加载时 private void ShowByFilter(AxMapControl sMapCtr, IFeatureLayer sFlyr, string sFilter) { ESRI.ArcGIS.Carto.IFeatureLayerDefinition pDef = (

实现多线程的两种方法:继承Thread类或实现Runnable接口

实现多线程的两种方法:继承Thread类或实现Runnable接口 Java中实现多线程有两种方法:继承Thread类和实现Runnable接口,在程序开发中只要是多线程,我们一般都是实现Runnable接口,原因归结为一点:实现接口比继承类要好. 多线程的第一种实现方式:继承Thread类 步骤如下 创建一个继承Thread的类(假定为A),并重写Thread的run方法 构造一个A类对象,假定为aa 调用aa的start方法.(start方法是从Thread继承过来的) 具体例子如下 pac

(转)java创建线程的两种方法比较

Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线程,有两种方法: ◆需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法:  ◆实现Runnalbe接口,重载Runnalbe接口中的run()方法. 为什么Java要提供两种方法来创建线程呢?它们都有哪些区别?相比而言,哪一种方法更好呢? 在Java中,类仅支持单继承

iOS 两种方法实现左右滑动出现侧边菜单栏 slide view

现在很多的APP中都有slide view,左右滑动出现侧边菜单栏的功能,Weico这个应用就有. 网上有很多第三方的类库实现了这种效果,其实自己代码写的话也是很简单的,下面我将介绍两种方法实现slide view.---- 一种是用第三方类库IIViewDeckController这个类库实现的效果比起其他的都好,另一种是自己代码实现这种效果,效果还ok. 实现方法一(使用第三方库IIViewDeckController): https://github.com/Inferis/ViewDec

C#实现Dll(OCX)控件自动注册的两种方法 网上找的 然后 自己试了试 还是可以用的

尽管MS为我们提供了丰富的.net framework库,我们的程序C#开发带来了极大的便利,但是有时候,一些特定功能的控件库还是需要由第三方提供或是自己编写.当需要用到Dll引用的时候,我们通常会通过“添加引用”的方式将它们纳入到项目中,然后就可以像使用自己的类一样方便的使用它们了.但是,有些Dll库(OCX)文件是需要注册到Windows注册表后才能正常添加和使用的.本文介绍两种为Dll库(OCX)自动注册的方法,为大家提供参考. 首先,大家都知道在Windows的“运行”中,输入“Regs

两种方法实现队满和队空的判断操作(循环队列)

本周的作业要求: 1.给出循环队列的存储结构定义. 2.完成循环队列的基本操作函数. 1)      初始化循环队列: 2)      建立循环队列: 3)      实现入队和出队操作: 4)     采用下面两种方法实现对满和队空的判断操作: 方法一:修改队满条件,浪费一个元素空间,队满时数组中只有一个空闲单元(必做):   方法二:设置标志flag,当front==rear且flag=0时为队空,当front==rear且flag=1时为队满(必做): 3.编写主函数实现基本操作函数功能,

C++连接mysql数据库的两种方法

现在正做一个接口,通过不同的连接字符串操作不同的数据库.要用到mysql数据库,以前没用过这个数据库,用access和sql server比较多.通过网上的一些资料和自己的摸索,大致清楚了C++连接mysql的方法.可以通过2种方法实现. 第一种方法是利用ADO连接, 第二种方法是利用mysql自己的api函数进行连接. 第一种方法可以实现我当前的需求,通过连接不同的字符串来连接不同的数据库.暂时只连接了mysql,sqlserver,oracle,access.对于access,因为它创建表的