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

为何GetMenuItemInfo获取菜单文本为空,GetMenuString却正常工作?

问题描述

使用GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo);获取菜单文本时,调试显示文本为空,但pMenuEdit->GetMenuString(i, strMenuText, MF_BYPOSITION);却能正常获取文本。微软文档推荐使用GetMenuItemInfo而非GetMenuString,请问哪里出错了?

代码片段:

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;
        }
    }
}

错误原因及修复方案

你调用GetMenuItemInfo时遗漏了关键步骤:未设置MIIM_STRING模式下必需的cch成员,也没有提前获取文本所需的缓冲区长度。

当使用MIIM_STRING标志时,Win32原生APIGetMenuItemInfo对缓冲区的要求是明确的:

  1. 如果dwTypeData设为NULL,函数会把cch设置为文本所需的字符数(不含终止符);
  2. 如果dwTypeData指向有效缓冲区,必须将cch设为缓冲区的容量,否则函数无法正确写入文本内容。

你的代码中,strMenuText.GetBuffer()未指定长度,默认分配的是空缓冲区,且sInfo.cch未初始化(值为0),导致GetMenuItemInfo没有空间写入文本,最终得到空字符串。

修复后的代码(标准两步法)

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 = nullptr;
        GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo);
        
        // 第二步:分配足够缓冲区并读取文本
        strMenuText.GetBuffer(sInfo.cch);
        sInfo.dwTypeData = strMenuText.GetBuffer();
        sInfo.cch += 1; // 预留终止符空间
        GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo);
        strMenuText.ReleaseBuffer();

        if (strMenuText == strCongregation)
        {
            PostMessage(WM_COMMAND, sInfo.wID);
            break;
        }
    }
}

简化写法(固定缓冲区)

如果能预估菜单文本的最大长度,可以直接分配足够大的缓冲区,省去一次API调用:

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;

        // 分配固定大小的缓冲区(按需调整)
        const int MAX_MENU_TEXT_LEN = 256;
        strMenuText.GetBuffer(MAX_MENU_TEXT_LEN);
        sInfo.dwTypeData = strMenuText.GetBuffer();
        sInfo.cch = MAX_MENU_TEXT_LEN;

        if (GetMenuItemInfo(pMenuEdit->GetSafeHmenu(), i, TRUE, &sInfo))
        {
            strMenuText.ReleaseBuffer();
            if (strMenuText == strCongregation)
            {
                PostMessage(WM_COMMAND, sInfo.wID);
                break;
            }
        }
    }
}

补充说明

GetMenuString能正常工作,是因为MFC封装了这个方法,内部自动处理了缓冲区的分配和长度计算;而GetMenuItemInfo是原生Win32 API,需要开发者手动管理缓冲区的大小,这也是它比GetMenuString更灵活的原因。

内容的提问来源于stack exchange,提问作者Andrew Truckle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:34:51