MFC自绘Menu的Bug

最近在使用MFC自绘菜单时遇到一个问题:当菜单项都为Popup Menu 时,发现MFC不会调用自绘MeasureItem,

以至于我的菜单宽度不对,无法显示菜单项文字。

跟踪发现: 在wincore.cpp中的OnMeasureItem函数中调用的_AfxFindPopupMenuFromID有点问题。OnMeasureItem是自绘菜单弹出时向菜单父窗口发送的WM_MEASUREITEM消息的响应函数,而_AfxFindPopupMenuFromID是个递归查找子菜单的函数,如果AfxFindPopupMenuFromID找到子菜单MFC就会调用菜单的MeasureItem。(wincore.cpp在vc安装目录下atlmfc/src/mfc目录中)

 _AfxFindPopupMenuFromID的代码如下:

AFX_STATIC CMenu* AFXAPI _AfxFindPopupMenuFromID(CMenu* pMenu, UINT nID)
{
    ENSURE_VALID(pMenu);
    
// walk through all items, looking for ID match
    UINT nItems = pMenu->GetMenuItemCount();
    
for (int iItem = 0; iItem < (int)nItems; iItem++)
    {
        CMenu
* pPopup = pMenu->GetSubMenu(iItem);
        
if (pPopup != NULL)
        {
            
if(pPopup->m_hMenu == (HMENU)(UINT_PTR)nID) {
                pMenu 
= CMenu::FromHandlePermanent(pMenu->m_hMenu);
                
return pMenu;
            }
            
// recurse to child popup
            pPopup = _AfxFindPopupMenuFromID(pPopup, nID);
            
// check popups on this popup
            if (pPopup != NULL)
                
return pPopup;
        }
        
else if (pMenu->GetMenuItemID(iItem) == nID)
        {
            
// it is a normal item inside our popup
            pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
            
return pMenu;
        }
    }
    
// not found
    return NULL;
}

可以看出这条函数只检查了pMenu的子菜单或菜单项而没有检查pMenu自身,所以当自绘菜单的菜单项只有子菜单就会导致查找不到菜单而不调用自绘菜单的MeasureItem函数。

修改方法: 可以在菜单的父窗口响应WM_MEASUREITEM消息,具体修改如下

CMenu*  MyFindPopupMenuFromID(CMenu* pMenu, UINT nID)
{
    ENSURE_VALID(pMenu);
    
// walk through all items, looking for ID match
    UINT nItems = pMenu->GetMenuItemCount();
    
for (int iItem = 0; iItem < (int)nItems; iItem++)
    {
        CMenu
* pPopup = pMenu->GetSubMenu(iItem);
        
if (pPopup != NULL)
        {
            
if(pPopup->m_hMenu == (HMENU)(UINT_PTR)nID) {
                pMenu 
= CMenu::FromHandlePermanent(pMenu->m_hMenu);
                
return pMenu;
            }
            
// recurse to child popup
            pPopup = MyFindPopupMenuFromID(pPopup, nID);
            
// check popups on this popup
            if (pPopup != NULL)
                
return pPopup;
        }
        
else if (pMenu->GetMenuItemID(iItem) == nID)
        {
            
// it is a normal item inside our popup
            pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
            
return pMenu;
        }
        
if(pMenu->GetSafeHmenu() == nID)
        {
            pMenu 
= CMenu::FromHandlePermanent(pMenu->m_hMenu);
            
return pMenu;            
        }
    }
    
// not found
    return NULL;
}

void CMyWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    
if (lpMeasureItemStruct->CtlType == ODT_MENU)
    {
        ASSERT(lpMeasureItemStruct
->CtlID == 0);
        CMenu
* pMenu=NULL;

        _AFX_THREAD_STATE
* pThreadState = _afxThreadState.GetData();
        
if (pThreadState->m_hTrackingWindow == m_hWnd)
        {
            
// start from popup
            pMenu = CMenu::FromHandle(pThreadState->m_hTrackingMenu);
        }
        
else
        {
            
// start from menubar
            pMenu = GetMenu();
        }

        ENSURE(pMenu);

        pMenu 
= MyFindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID);
        
if (pMenu != NULL)
        {
            pMenu
->MeasureItem(lpMeasureItemStruct);
        }
        
else
        {
            TRACE(traceAppMsg, 
0"Warning: unknown WM_MEASUREITEM for menu item 0x%04X. ",
                lpMeasureItemStruct
->itemID);
        }
    }
    
else
    {
        CWnd
* pChild = GetDescendantWindow(lpMeasureItemStruct->CtlID, TRUE);
        
if (pChild != NULL && pChild->SendChildNotifyLastMsg())
            
return;     // eaten by child
    }
    
// not handled - do default
    Default();
}

可以看出其实只在查找函数中多加了一个判断。至于为什么用HMENU和id比较,是因为如果是子菜单的话,id就是子菜单的句柄。

阅读更多
文章标签: mfc menu thread null
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

MFC自绘Menu的Bug

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭