如何在MFC中检测CMenu可见状态及获取菜单消失事件?
检查右键菜单状态与捕获菜单消失事件
一、判断菜单是否处于显示状态
CMenu类确实没有直接提供检查菜单是否显示的方法,但可以通过Windows API结合窗口特性实现:
利用菜单窗口的类名特性:Windows弹出菜单对应的窗口类名为
#32768,可通过查找该类窗口并关联菜单句柄判断:BOOL IsPopupMenuVisible(CMenu* pMenu) { if (!pMenu || !pMenu->GetSafeHmenu()) return FALSE; // 查找系统菜单窗口 HWND hMenuWindow = FindWindow(_T("#32768"), NULL); if (hMenuWindow) { // 获取菜单窗口关联的菜单句柄(需在弹出前提前设置) HMENU hAttachedMenu = (HMENU)GetWindowLongPtr(hMenuWindow, GWLP_USERDATA); if (hAttachedMenu == pMenu->GetSafeHmenu()) { return IsWindowVisible(hMenuWindow); } } return FALSE; }使用时,在弹出菜单前绑定菜单句柄到窗口用户数据:
void ShowMyPopupMenu(CPoint point) { CMenu menu; menu.LoadMenu(IDR_MY_POPUP); CMenu* pPopup = menu.GetSubMenu(0); HMENU hMenu = pPopup->GetSafeHmenu(); // 阻塞模式弹出菜单,函数返回前菜单处于显示状态 UINT cmd = pPopup->TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN, point.x, point.y, this); }间接判断逻辑:如果使用
TrackPopupMenu默认阻塞模式,函数返回前菜单一定显示,返回后则已关闭,可通过函数执行状态间接判断。
二、捕获菜单消失事件
方法1:处理WM_EXITMENULOOP消息
系统会在菜单循环退出(菜单关闭)时,向菜单所有者窗口发送WM_EXITMENULOOP消息,这是最简洁的实现方式:
在窗口类消息映射中添加处理:
BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) ON_MESSAGE(WM_EXITMENULOOP, OnExitMenuLoop) // 其他消息映射 END_MESSAGE_MAP()实现消息处理函数:
void CMyWnd::OnExitMenuLoop(UINT nFlags) { // 菜单已关闭,执行业务逻辑 CFrameWnd::OnExitMenuLoop(nFlags); }
方法2:使用窗口钩子监控菜单销毁
如需精细化控制(比如监控多个菜单),可使用WH_CALLWNDPROC钩子跟踪菜单窗口销毁:
HHOOK g_hMenuHook = nullptr; HMENU g_hTargetMenu = nullptr; LRESULT CALLBACK MenuHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) { CWPSTRUCT* pMsg = reinterpret_cast<CWPSTRUCT*>(lParam); if (pMsg->message == WM_DESTROY && ::GetClassName(pMsg->hwnd) == _T("#32768")) { HMENU hMenu = reinterpret_cast<HMENU>(::GetWindowLongPtr(pMsg->hwnd, GWLP_USERDATA)); if (hMenu == g_hTargetMenu) { // 目标菜单已销毁,执行处理逻辑 AfxMessageBox(_T("右键菜单已关闭")); ::UnhookWindowsHookEx(g_hMenuHook); g_hMenuHook = nullptr; } } } return ::CallNextHookEx(g_hMenuHook, nCode, wParam, lParam); } // 弹出菜单时设置钩子 void CMyWnd::ShowPopup(CPoint point) { CMenu menu; menu.LoadMenu(IDR_POPUP_MENU); CMenu* pPopup = menu.GetSubMenu(0); g_hTargetMenu = pPopup->GetSafeHmenu(); // 设置线程钩子,监控当前线程窗口消息 g_hMenuHook = ::SetWindowsHookEx(WH_CALLWNDPROC, MenuHookProc, AfxGetInstanceHandle(), ::GetCurrentThreadId()); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN, point.x, point.y, this); }
内容的提问来源于stack exchange,提问作者ssroreo
相关产品推荐
相关产品推荐

