Windows的窗口菜单是一个比较常用的资源,通常的做法都是在先创建菜单,然后鼠标点击窗口的某个位置时显示菜单,然后点击菜单项,向窗口发送WM_COMMAND消息.如下代码片段所示:

 
  
  1. ... 
  2. m_hWinMenu = ::LoadMenu(::GetInstance(), MAKEINTRESOURCE(IDM_MAINMENU));//A 
  3. ... 
  4. ... 
  5. case WM_EXITMENULOOP: 
  6.     { 
  7.         ...//C 
  8.     } 
  9.     break
  10. case WM_LBUTTONUP: 
  11.     { 
  12.         ... 
  13.         ::TrackPopupMenu(hDropFileMenu, TPM_RIGHTBUTTON, 
  14. ptl.x, ptl.y, 0, m_hWnd, NULL);//B 
  15.         ... 
  16.     } 
  17.     break
  18. case WM_COMMAND 
  19.     { 
  20.         // Command is not from menu. 
  21.         if (HIWORD(wParam) !=0 ) return;//D 
  22.         ... 
  23.     } 
  24. ... 
  25. ... 

 

  上面的代码片段是一个典型的菜单使用模式: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函数也可以同步执行,因为调用这个函数的线程会阻塞在这个函数中,然后给用户显示一个菜单,此时,用户一般都户点击某个菜单项,函数返回,这并不会影响程序 的运行;即使用户不点击菜单项,而点击其它地方,该函数一样会返还,只是不会返回任何菜单项的命令,所有也不会影响程序的运行.有时候不得不佩服微软的人思虑周全.