visual-c++mfcmenuitem

Detecting menu item text (and ID)


Code:

auto pMenuEdit = GetCongregationSubMenu(1);
if (pMenuEdit != nullptr)
{
    const int iSize = pMenuEdit->GetMenuItemCount();
    for (int i = 0; i < iSize; i++)
    {
        CString strMenuText;

        MENUITEMINFO sInfo{};
        sInfo.cbSize = sizeof(MENUITEMINFO);
        sInfo.fMask = MIIM_STRING | MIIM_ID;
        sInfo.dwTypeData = strMenuText.GetBuffer();
        GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo);
        strMenuText.ReleaseBuffer();

        if (strMenuText == strCongregation)
        {
            // Simulate pressing the Edit Congregation menu item
            PostMessage(WM_COMMAND, sInfo.wID);
            break;
        }

        pMenuEdit->GetMenuString(i, strMenuText, MF_BYPOSITION);
        if (strMenuText == strCongregation)
        {
            // Simulate pressing the Edit Congregation menu item
            PostMessage(WM_COMMAND, sInfo.wID);
            break;
        }
    }
}

Why is it that the pMenuEdit->GetMenuString(i, strMenuText, MF_BYPOSITION); approach works correctly but the GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo); approach does not? According to the debugger the text is empty.

The Microsoft documentation encourage using GetMenuItemInfo instead of GetMenuString.

My mistake?


Solution

  • Text buffers in C always consist of two parts: A pointer to the beginning of the buffer and a count (in elements or bytes). The code in question never supplies a value for the cch field of the MENUITEMINFO structure.

    Solving this will still cause the code to fail to retrieve the menu item text because the strMenuText buffer is empty. You also must allocate sufficient space (e.g., by supplying a buffer size in the call to CString::GetBuffer()).

    The standard pattern is to discover the required length first:

    MENUITEMINFO sInfo{};
    sInfo.cbSize = sizeof(MENUITEMINFO);
    sInfo.fMask = MIIM_STRING | MIIM_ID;
    if (!GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo)) {
        // Handle error
    }
    
    UINT const required_length = sInfo.cch;
    

    and use that information to allocate a sufficiently sized buffer:

    CString strMenuText;
    
    sInfo.dwTypeData = strMenuText.GetBuffer(required_length);
    sInfo.cch = required_length + 1;
    if (!GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo)) {
        // Handle error
    }
    strMenuText.ReleaseBufferSetLength(sInfo.cch);