为何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对缓冲区的要求是明确的:
- 如果
dwTypeData设为NULL,函数会把cch设置为文本所需的字符数(不含终止符); - 如果
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
相关产品推荐
相关产品推荐

