当前位置:C++技术网 > 精选软件 > C++实现软件最小化关闭到右下角通知栏托盘并响应的详解

C++实现软件最小化关闭到右下角通知栏托盘并响应的详解

更新时间:2016-11-17 17:41:47浏览次数:1+次

    如果一个软件需要长期后台运行,比如杀毒软件、音乐播放器、服务器程序,一般都需要显示在通知栏里,也叫作托盘里。此时就不在任务栏里显示了。任务栏里显示的东西,始终给人的感觉就是临时的,不是一个稳态。当然,这是我的感觉,每一个人可能感觉不太一样。因为任务栏里的程序很容易被你操作,并且会关掉它。对于一些后台软件或者需要长时间运行的软件,就容易被误关闭了。你可以阻止关闭,加一个提醒,不过,如果直接将其显示到通知栏托盘里,效果更好。然后在通知栏托盘里是一个推荐的做法。

    因为我做的这个软件是服务器后台软件,所以需要长时间运行。然后就用上了。顺便将C++的实现方法分享一下。毕竟,C++实现起来灵活,有人会感觉比较难的样子。这里写出实现方法,供大家使用,也供自己后续使用。

    下面是效果图:

C++实现软件最小化关闭到右下角托盘(通知栏)区域并响应

    总体是思路是:

1.在最小化和关闭的时候将窗口隐藏,并向通知栏托盘添加图标

    如果你直接使用Win32在做,就处理WM_SYSCOMMAND消息来响应最小化和关闭。如果是MFC即VC,那么在重载的虚函数OnSysCommand里处理即可。当然其他环境,参考Win32来做,也就是处理这个消息就行。

    MFC默认的处理是这样的:


void 主窗口类名::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}
    我们就在这里加入处理即可,通过nID参数即可比较得到。最小化和关闭分别为:SC_MINIMIZE、SC_CLOSE。


    然后就是向通知栏托盘添加图标以及响应图标消息的相关信息。整个代码如下:


void 主窗口类名::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
    else if(nID==SC_MINIMIZE || nID==SC_CLOSE)
    {
        //向通知栏托盘添加图标
        NOTIFYICONDATA nid={0};
        nid.cbSize=sizeof(NOTIFYICONDATA);
        nid.hWnd=m_hWnd;
        nid.uID=IDR_MAINFRAME;
        nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP ;
        nid.uCallbackMessage=WM_MYMSG;//自定义的响应托盘图标的消息
        nid.hIcon=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));//这里使用应用程序图标
        _tcscpy(nid.szTip,_T("双击显示 充电桩站级监控系统 主界面"));
        Shell_NotifyIcon(NIM_ADD,&nid);//在托盘区添加图标
        ShowWindow(SW_HIDE);//隐藏主窗口
    }
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}
    这里是介绍整个实现过程,NOTIFYICONDATA结构体就不介绍了,简单看看MSDN即可。需要提一下的就是uCallbackMessage。这个接受的值是我们自定义的消息ID。也就是说,当图标被操作的时候,系统会向窗口发送这个自定义消息。然后会传入标准的消息的参数,如右击、单击、双击等等。因为自定义消息宏没有被定义,所以我们要先定义一下:



#define WM_MYMSG WM_USER+1


2.提供通知栏托盘图标响应的自定义消息的处理函数

    处理函数就和普通的消息处理函数一样,只是我们要自己手动的添加下消息映射。头文件中的处理函数声明如下:


afx_msg LRESULT OnHandleIcon(WPARAM wParam,LPARAM lParam);
    源文件中的消息映射如下:



BEGIN_MESSAGE_MAP(主窗口类名, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
    ON_MESSAGE(WM_MYMSG,OnHandleIcon)//自定义消息的映射
END_MESSAGE_MAP()
    然后就是自定义消息的处理函数实现:



LRESULT 主窗口类名::OnHandleIcon(WPARAM wParam,LPARAM lParam)
{
    return FALSE;
}



3.操作通知栏托盘图标,启动菜单,响应菜单

    到上面为止,消息处理的环境搭建起来了。而且,我们也实现了在通知栏托盘显示图标。现在就需要操作通知栏托盘里的图标了。实际上也就是实现OnHandleIcon函数的代码。

    OnHandleIcon函数第一个参数是图标ID,第二个参数是操作的消息如WM_LBUTTONDBLCLK。那么处理代码是简单的,如下所示:

LRESULT 主窗口类名::OnHandleIcon(WPARAM wParam,LPARAM lParam)
{
    //wParam:图标ID,lParam:消息
    if(wParam!=IDR_MAINFRAME)return TRUE;
    switch(lParam)
    {
    case WM_RBUTTONDOWN://右击创建菜单
        {
            CPoint pt;
            GetCursorPos(&pt);
            CMenu menu;
            menu.CreatePopupMenu();
            menu.AppendMenu(MF_STRING,WM_DESTROY,_T("退出系统")); //直接可以依赖系统的退出消息处理,而无需我们自己添加一个菜单处理,比较省事
            menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_BOTTOMALIGN,pt.x,pt.y,this);
            menu.DestroyMenu();
        }
        break;
    case WM_LBUTTONDBLCLK://双击显示主窗口
        {
            //先最小化,再正常显示,可以随时将主窗口显示出来。如果已经显示了主窗口,只是被遮住了,也可以拉出来。这里你还可以直接激活、设置焦点、将主窗口带回顶部,对应了几个函数,效果可以自测。
            ShowWindow(SW_MINIMIZE);
            ShowWindow(SW_SHOWNORMAL);
        }
        break;
    }
    return FALSE;
}
    


4.退出程序时删除通知栏托盘图标

    程序退出时,也就是单击了“退出系统”菜单项。这时消息跑到了系统的销毁窗口处理。MFC直接重载DestroyWindow函数即可。Win32就是处理WM_DESTROY消息。

    如果我们不主动删除通知栏托盘图标,我们程序退出后,依然可以看到图标。然后通知栏托盘被系统重画之后,比如鼠标经过,才会删除图标。这样体验是不好的,要及时删除,别让系统帮你擦屁股哦。

    删除通知栏托盘图标的代码和添加是很相似的。不过更加简单,你指定清楚资源ID就行了,结构体里的其他东西都可以不填。代码如下:


BOOL 主窗口类名::DestroyWindow()
{
    NOTIFYICONDATA nid;//这里的nid只是演示删除图标的使用。要想实际删除托盘图标,一定要用创建的托盘图标的nid。一般是放在成员变量中存储起来。
    nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
    nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));//资源ID
    //前面3行代码是演示删除图标完整代码,实际上删除不需要上面3行
    Shell_NotifyIcon(NIM_DELETE,&nid);//删除已经添加图标的nid
    return CDialogEx::DestroyWindow();
}
    这已经是非常精简的代码了。所以看上去很清楚。


    到此,整个实现就完成了。