在MFC程序中,在对话框进行大量的右键菜单操作之后,出现右键菜单无法弹出的问题。

在MFC程序中,在对话框进行大量的右键菜单操作之后,出现右键菜单无法弹出的问题。
代码如下:

void CMyDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
     CMenu menu;
     menu.CreatePopupMenu();
     menu. AppendMenu();
      ……
     menu. AppendMenu();
     // 多个子菜单
     CMenu menuSub1;
     menuSub1.AppendMenu();
     ……
     menu.AppendMenu(MF_STRING | MF_POPUP | MF_BYPOSITION, (UINT)menuSub1.m_hMenu,”子菜单1);
     menuSub1.Detach();
     CMenu menuSub2;
     menuSub2.AppendMenu();
     ……
     menu.AppendMenu(MF_STRING | MF_POPUP | MF_BYPOSITION, (UINT)menuSub2.m_hMenu,”子菜单2);
     menuSub2.Detach();
      .....
      menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
menu.Detach();
	CDialogEx::OnRButtonDown(nFlags, point);
}

检查代码,似乎没有什么问题,顿时一愁莫展。细想一下,这个Bug不像普通的逻辑错误的Bug那样,通过简单的几步操作就出现,而是要操作一定的时间后,才会出现。这时会联想到内存泄漏,内存泄漏也会存在类似的情况,每次很少的泄漏,操作一段时间后,就会有量的积累,程序的Bug才会爆发死机等问题。根据此思路,再次检查,发现程序并没有内存分配,根本不存在内存泄漏的问题。MFC程序中,还有一个与内存泄漏类似的问题,就是资源泄漏,这时把目标定位转移到资源泄漏上。再次检查代码,初看也没看出有资源泄漏的问题,比如没有CDC、CPen和CBrush等资源。但是,这里一个CMenu,菜单是一种资源,API通过HMENU来引用它,CMenu对HMENU进行了封装。通过分析,估计Bug极有可能是CMenu引起的。现在来好好分析一下代码,OnRButtonDown中定义了弹出菜单menu,以及多个子菜单menuSub1,menuSub2……,菜单的声明及构造代码没有问题,只剩下 Detach()函数嫌疑最大了。是不是该函数的问题?先分析一下Detach()函数。之前看过的C++书籍在说,当菜单是个局部对象,要调用菜单的Detach()函数,使菜单句柄与菜单对象分离,这样当菜单对象销毁后,菜单窗口仍然存在。

HMENU CMenu::Detach()
{
    HMENU h = m_hMenu;
    m_hMenu = NULL;
 
    return h;
}

m_hMenu是句柄,在Windows中句柄是一种特殊的智能指针,指出操作系统管理的内存块或对象。Detach函数内仅把当前对象的m_hMenu赋值为Null,但原m_hMenu这个句柄就游离在程序的控制之外,有点像已分配的内存,没释放指向它的指针被置为Null一样。先按上述推论对代码进行修改,把所有Detach()函数都去掉,经测试再没出现上述问题。
为了验证menu作为局部变量要加Detach()函数的说法,从重查阅了一下孙鑫的《C++深入详解》。原书例子:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	....
	menu.LoadMenu(IDR_MAINFRAME);
	setMenu(&menu);
	menu.Detach();
	return 0;
}

其说明如下:

在设置窗口菜单时,如果定义的是局部菜单对象,则一定要在调用SetMenu函数设置窗口菜单后,立即调用对象的Detach函数将菜单句柄与菜单分离。

这里有一个前题条件是调用SetMenu函数之后,并不是所有的局部菜单都要加Detach函数。本来是一个简单的问题,却给自己造成这么大的困惑,是要认真总结一下经验教训。平时看书不够仔细,如果不对知识点理解,有时照抄照搬也会差之毫厘,谬之千里。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值