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

 

posted @ 2014-05-06 13:50  struggle_time  阅读(1356)  评论(3编辑  收藏  举报