MFC定时关机程序的实现3-最小化到托盘栏
这个定时关机运行过后默认最小化到托盘栏最好了,不用每次都去点了。
现在来看看如何将程序显示在托盘栏。
首先在头文件里声明一个变量和一个消息响应函数
1 //最小化到托盘栏 2 //第一步,生成一个成员变量,或者一个全局变量 3 NOTIFYICONDATA m_NOTIFYICON; 4 //第二步,添加自定义消息响应函数 5 afx_msg LRESULT OnNotifyIcon(WPARAM wParam,LPARAM lParam);
然后初始化消息相关的参数
//第三步,添加消息标识 #define WM_NC WM_USER + 1001
现在添加消息映射
//第四步,添加消息映射 ON_MESSAGE(WM_NC, &OnNotifyIcon)
最后定义消息响应函数,这里主要是做一些相应鼠标操作,比如左键单击,右键单击等的事件
LRESULT CAutoShutDownDlg::OnNotifyIcon(WPARAM wParam,LPARAM lParam) { ////最小化图标,,wParam接收的是图标的ID,而lParam接收的是鼠标的行为 if (wParam == IDI_ICON1) { return 1; } switch(lParam) { case WM_LBUTTONDOWN: { //鼠标单击图标时的动作 if (AfxGetApp()->m_pMainWnd->IsWindowVisible())//判断窗口当前状态 { //窗口未最小化 AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE); } else { AfxGetApp()->m_pMainWnd->ShowWindow(SW_SHOW); } } break; case WM_RBUTTONUP: { ////右键起来时弹出快捷菜单,这里只有一个“关闭” LPPOINT lp = new tagPOINT;//鼠标位置结构 ::GetCursorPos(lp);//获得鼠标位置 CMenu menu; menu.CreatePopupMenu();//声明一个弹出式菜单 //增加菜单项“退出”,点击则发送消息WM_DESTROY给主窗口(已隐藏),将程序结束。 menu.AppendMenu(MF_STRING,WM_DESTROY,"退出"); menu.TrackPopupMenu(TPM_LEFTALIGN,lp->x,lp->y,this); //资源回收 HMENU hmenu = menu.Detach(); menu.DestroyMenu(); delete lp; } break; default: break; } return 0; }
这些操作完成后还要有一个初始化操作OnInitDialog()
//最小化到托盘代码初始化 m_NOTIFYICON.cbSize = sizeof(NOTIFYICONDATA);//这个是必须的,指定结构大小 m_NOTIFYICON.hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);//指定在托盘显示的图标,这个图标可以自己导入 m_NOTIFYICON.hWnd = m_hWnd;//指定窗口句柄 m_NOTIFYICON.uID = IDR_MAINFRAME;//托盘图标ID m_NOTIFYICON.uCallbackMessage = WM_NC;//自定义的消息函数 lstrcpy(m_NOTIFYICON.szTip,"定时关机");//在托盘栏显示的提示信息 m_NOTIFYICON.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;// Shell_NotifyIcon(NIM_ADD,&m_NOTIFYICON);//在托盘显示图标
最后一点就是在程序退出时销毁图标
1 //退出程序时销毁图标 2 Shell_NotifyIcon(NIM_DELETE,&m_NOTIFYICON);//
这里面用到了一个结构 NOTIFYICONDATA
1 typedef struct _NOTIFYICONDATA { 2 DWORD cbSize; // 结构的大小,必须在程序中给出 3 HWND hWnd; // 程序中将要接收托盘消息的窗口句柄 4 UINT uID; // 应用程序中定义的托盘图标ID,此参数用作标识 5 UINT uFlags; //设置属性 标记下边3个参数是否有效 6 UINT uCallbackMessage;// 自定义的消息ID值 7 HICON hIcon;//显示在系统托盘上的Icon的句柄 8 #if (_WIN32_IE < 0x0500) 9 TCHAR szTip[64;// 用于图标显示的提示字符串 10 #else 11 TCHAR szTip[128]; 12 #endif 13 #if (_WIN32_IE >= 0x0500) 14 DWORD dwState; 15 DWORD dwStateMask; 16 TCHAR szInfo[256]; 17 union { 18 UINT uTimeout; 19 UINT uVersion; 20 } DUMMYUNIONNAME; 21 TCHAR szInfoTitle[64]; 22 DWORD dwInfoFlags; 23 #endif 24 #if (_WIN32_IE >= 0x600) 25 GUID guidItem; 26 #endif 27 } NOTIFYICONDATA, *PNOTIFYICONDATA;
后面的一些参数默认就好,不要配置
还有一点就是怎么保证程序只有一个进程在运行
有两种方法可以实现,第一通过全局互斥变量,第二通过查找窗口名
第一,全局互斥变量
在应用程序类中声明一个全局变量
HANDLE hMutex;
然后在应InitInstance()函数中添加如下代码
1 hMutex = ::CreateMutex(NULL,TRUE,"FIRSTDLG"); 2 if(hMutex != NULL) 3 { 4 if (GetLastError() == ERROR_ALREADY_EXISTS) 5 { 6 AfxMessageBox("已经有一个程序在运行了"); 7 exit(0); 8 } 9 }
第二,findwindow函数实现
添加一个成员函数,函数定义如下,只需要在初始化时判断一下就行了
1 BOOL CAutoShutDownApp::FirstInstance() 2 { 3 CWnd *pWndPrev, *pWndChild; 4 5 // 通过窗口标题查找窗口是否已经打开。成功则返回指向窗口的句柄,否则为NULL 6 pWndPrev = CWnd::FindWindow(NULL, "定时关机"); 7 if (NULL != pWndPrev) 8 { 9 // 存在这个窗口,获取上一次的状态 10 pWndChild = pWndPrev->GetLastActivePopup(); 11 12 // 是否最小化了,还原 13 if (pWndPrev->IsIconic()) 14 pWndPrev->ShowWindow(SW_RESTORE); 15 16 //显示到最上层 17 pWndChild->SetForegroundWindow(); 18 19 return FALSE; 20 } 21 22 return TRUE; 23 }
在初始化时进行查找,在InitInstance()函数里添加如下代码
if (!FirstInstance()) { return FALSE; }
当然还有其他方法,不过这两种比较简单。
FindWindow()函数定义如下,两个参数必须至少有一个有效
1 static CWnd* PASCAL FindWindow( 2 LPCTSTR lpszClassName,//窗口类名 3 LPCTSTR lpszWindowName //窗口标题 4 );//
还有一个问题要说的,就是程序启动后自动隐藏的情况,原来是想着直接在里面ShowWindow(SWP_HIDE);来实现的,不过很遗憾这条语句根本不起作用,因为窗口的现实是在初始化之后的某个地方显示的。
GetWindowPlacement,SetWindowPlacement这两个函数可以实现这个功能。
过程如下,首先我们需要一个成员变量来保存这个窗口的一些配置信息。
WINDOWPLACEMENT m_wp;
在OnInitDialog中进行初始化
1 //程序启动后自动隐藏到托盘栏 2 GetWindowPlacement(&m_wp); //恢复时用 3 ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);//从任务栏中去掉. 4 5 WINDOWPLACEMENT wp;//声明结构 6 wp.length=sizeof(WINDOWPLACEMENT);//制定大小 7 wp.flags=WPF_RESTORETOMAXIMIZED;//标志 8 wp.showCmd=SW_HIDE;//状态 9 SetWindowPlacement(&wp);//设置隐藏
然后在OnNotifyIcon()函数中进行鼠标相应
1 LRESULT CAutoShutDownDlg::OnNotifyIcon(WPARAM wParam,LPARAM lParam) 2 { 3 ////最小化图标,,wParam接收的是图标的ID,而lParam接收的是鼠标的行为 4 if (wParam == IDI_ICON1) 5 { 6 return 1; 7 } 8 switch(lParam) 9 { 10 case WM_LBUTTONDOWN: 11 { 12 //鼠标单击图标时的动作 13 if (AfxGetApp()->m_pMainWnd->IsWindowVisible())//判断窗口当前状态 14 { 15 //窗口未最小化 16 AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE); 17 } 18 else 19 { 20 AfxGetApp()->m_pMainWnd->ShowWindow(SW_SHOW); 21 22 m_wp.length=sizeof(WINDOWPLACEMENT); 23 m_wp.flags=WPF_RESTORETOMAXIMIZED; 24 m_wp.showCmd=SW_SHOW; 25 SetWindowPlacement(&m_wp); 26 // 在这里恢复显示状态 27 } 28 29 } 30 break; 31 case WM_RBUTTONUP: 32 33 34 ............................. 35 36 37 38 }
现在可以实现启动后隐藏了,但是还有一个问题那就左键单击图标后,窗口显示在最上层了,但是标题栏却是未激活状态,没有重绘。所以接下来添加一个重绘框架函数
OnNcPaint()
void CAutoShutDownDlg::OnNcPaint() { // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CDialogEx::OnNcPaint() static int i = 2; if (i>0) { i--; ShowWindow(SW_HIDE); } else CDialogEx::OnNcPaint(); }
到现在基本完成了。
这里是源代码,这个博客园的文件上传貌似不太好用,就用百度云了,运行环境是vs2010+win7 64位
http://pan.baidu.com/s/1i35gqET
本文来自博客园,作者:struggle_time,转载请注明原文链接:https://www.cnblogs.com/songliquan/p/3711259.html