CMenu-关于菜单的销毁操作

来源:互联网 发布:nba09年科比季后赛数据 编辑:程序博客网 时间:2024/05/10 06:38

今天扫了下CMenu的MSDN和源代码,真好啊它是从CObject继承下来的。这里仅说下菜单销毁时要注意的地方。

其实在堆中还是栈中使用CMenu不是重点要注意的地方,我们要清楚的是CMenu对象超出作用域时会默默执行什么操作以及应用的场合。

所以当然要先看下析构函数:

_AFXWIN_INLINE CMenu::~CMenu()
 { DestroyMenu(); }

BOOL CMenu::DestroyMenu()
{
 if (m_hMenu == NULL)
  return FALSE;
 return ::DestroyMenu(Detach());
}

HMENU CMenu::Detach()
{
 HMENU hMenu;
 if ((hMenu = m_hMenu) != NULL)
 {
  CHandleMap* pMap = afxMapHMENU(); // don't create if not exist
  if (pMap != NULL)
   pMap->RemoveHandle(m_hMenu);
 }
 m_hMenu = NULL;
 return hMenu;
}

其中m_hMenu是CMenu类封装的菜单句柄。可以看出对象一旦超出作用域,菜单句柄就会尝试被销毁。

1. 先来看一种应用场合:在视类客户区按下右键弹出快捷菜单

CMyView::OnContextMenu {

CMenu menu;

menu.LoadMenu(); // 创建菜单句柄并绑定到CMeun对象上

menu.GetSubMenu(0)->TracePopupMenu(); // 弹出右键快捷菜单

CView::OnContextMenu; // 调用基类

}

因为CMenu对象存在于栈中,故每次函数退出系统都调用其析构销毁菜单句柄,每次进入函数又会重新创建对象并绑定句柄,所以这样写是ok的。

2. 再来看另一种应用场合:将一个top-level菜单绑定到主窗口上

void CMainFrame::OnReplaceMenu()
{
   // Load the new menu.
   m_NewMenu.LoadMenu(IDR_SHORT_MENU);
   ASSERT(m_NewMenu);

   // Remove and destroy the old menu
   SetMenu(NULL); 
   ::DestroyMenu(m_hMenuDefault); 

   // Add the new menu
   SetMenu(&m_NewMenu);

   // Assign default menu
   m_hMenuDefault = m_NewMenu.GetSafeHmenu();  // or m_NewMenu.m_hMenu;
}
这是MSDN中给的例子,调用CWnd::SetMenu为窗口更换菜单时要注意,必须自己释放掉脱离窗口的先前菜单占用的资源。

[MSDN: SetMenu will not destroy a previous menu. An application should call theCMenu::DestroyMenu member function to accomplish this task.]

而最后绑定在窗口上的菜单,调用DestroyMenu显式销毁菜单不是必须的(但通常我们会显示调用)。因为窗口销毁时会尝试销毁绑定着的菜单,正如主窗口创建时会创建并绑定一个(主)菜单(CWnd::LoadFrame(IDR_MAINFRAME)创建窗口时会创建并绑定资源名为IDR_MAINFRAME的菜单作为默认菜单使用)那样默默进行。我们要注意的仅是更换菜单时别忘记要销毁被更换下的菜单资源就好了,像上例那样。

3. 再来看另一种应用场合:还是将一个top-level菜单绑定到主窗口上

上例中m_NewMenu是成员变量,所要在窗口类的大多数成员函数内使用都没有问题(都不会超出作用域)。但为主窗口绑定菜单还有另一种可能,

CMainFrame::OnCreate {

CMenu menu;

menu.LoadMenu(); // 创建菜单句柄并绑定到CMeun对象上

SetMenu(&menu);

menu.Detach(); // 这句是必须的

}

因为CMenu对象是个临时变量,如果没有调用CMenu::Detach(),当OnCrete函数调用完成返回后,menu对象就超出其作用域,系统会自动调用其析构尝试销毁菜单资源。可是我们已经将这个菜单资源又绑定跟主窗口了,这就会造成异常。如果调用了CMenu::Detach(),则菜单句柄就可以脱离menu对象,当menu对象销毁时并不会销毁菜单资源,这样主窗口就可以放心使用菜单资源了,当窗口被销毁时,窗口自己会负责菜单的释放。