You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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消息,这是最简洁的实现方式:

  1. 在窗口类消息映射中添加处理:

    BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)
        ON_MESSAGE(WM_EXITMENULOOP, OnExitMenuLoop)
        // 其他消息映射
    END_MESSAGE_MAP()
    
  2. 实现消息处理函数:

    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:10:24