Windows的窗口菜单是一个比较常用的资源,通常的做法都是在先创建菜单,然后鼠标点击窗口的某个位置时显示菜单,然后点击菜单项,向窗口发送WM_COMMAND消息.如下代码片段所示:
- ...
- m_hWinMenu = ::LoadMenu(::GetInstance(), MAKEINTRESOURCE(IDM_MAINMENU));//A
- ...
- {
- ...
- case WM_EXITMENULOOP:
- {
- ...//C
- }
- break;
- case WM_LBUTTONUP:
- {
- ...
- ::TrackPopupMenu(hDropFileMenu, TPM_RIGHTBUTTON,
- ptl.x, ptl.y, 0, m_hWnd, NULL);//B
- ...
- }
- break;
- case WM_COMMAND
- {
- // Command is not from menu.
- if (HIWORD(wParam) !=0 ) return;//D
- ...
- }
- ...
- }
- ...
上面的代码片段是一个典型的菜单使用模式:A行先创建菜单,B行显示菜单,然后点击某个菜单项,执行到D行.不过怎么没有提到过C行呢?
C行是在消息WM_EXITMENULOOP中处理的,它是释放菜单时其窗口收到的一个消息,关键问题是,这个消息上面时候发出?
按照一般的逻辑思维,WM_EXITMENULOOP应该在WM_COMMAND之后,用户点击一个菜单项,发出WM_COMMAND消息,然后处理,最后释放菜单.若如此想的话就大错特错了,经过笔者测试发现,WM_EXITMENULOOP居然
在WM_COMMAND之前发生,也就是如果用户点击了某个菜单项,其窗口先收到的是WM_EXITMENULOOP,然后才收到WM_COMMAND,这就导致下面一个问题:
当时笔者的程序有一个特点: 在菜单出现之前动态分配了一个内存(必须的,没有办法修改),然后在WM_LBUTTONUP时弹出一个菜单,下面有两个选择:
1)如果用户点击某个菜单项,窗口收到WM_COMMAND消息处理这个内存,之后,程序必须释放这个动态分配的内存;
2)如果用户不点击该菜单项,而且单击任意地方,菜单被释放,然后消失,程序也必须释放这个动态分配的内存;
这两个选择令人纠结的地方在于:
无论哪个选择,窗口必然能够收到WM_EXITMENULOOP消息,但是我们不能在这个地方释放这块内存,否则,如果是第一个选择,那么可能程序崩溃(内存已经被释放);可是如果不在这个地方释放,
在哪里释放呢?如果在WM_COMMAND消息中释放,可用户可以做第2个选择,这样窗口收不到WM_COMMAND,则内存泄露,是不是很令人蛋疼?
不过好在Window并未绝人之路,因为Windows为TrackPopupMenu提供了一个选项:TPM_RETURNCMD,这个选项会导致这个函数同步执行,它的返回值就是用户选择的命令ID,不过此时菜单的窗口就收不到WM_COMMAND了.
事后仔细想想,windows将WM_EXITMENULOOP放在WM_COMMAND之前执行还是有道理的,因为程序在WM_COMMAND中很可能是一个同步操作,且时间比较长,这时如果还未执行WM_EXITMENULOOP,那么菜单将不会
被释放,也不会消失,这样的界面很不友好.另外,TrackPopupMenu函数也可以同步执行,因为调用这个函数的线程会阻塞在这个函数中,然后给用户显示一个菜单,此时,用户一般都户点击某个菜单项,函数返回,这并不会影响程序
的运行;即使用户不点击菜单项,而点击其它地方,该函数一样会返还,只是不会返回任何菜单项的命令,所有也不会影响程序的运行.有时候不得不佩服微软的人思虑周全.
转载于:https://blog.51cto.com/jetyi/640836