对mdi程序中一个弹出菜单警告原因的分析

laomai 2007-10-26 11:33:25
对mdi程序中一个弹出菜单警告原因的分析
作者: laomai
网址: http://blog.csdn.net/laomai
(转载时请注明出处)
一、引子
最近在编译一个别人的mdi程序代码,在调试程序时,vc6的output窗口实现一个了提示
"Warning: GetWindowMenuPopup failed! "
为了找出产生这个警告的原因,查了不少资料,并仔细跟踪了有关的mfc代码,
终于找到了这个警告产生的原因,现将我的探索过程记录一下,供大家分享。
二、产生该警告的代码
由于这个提示是在运行时出现,自然首先要看看什么代码输出了这个警告。在
mfc的源代码目录中(对于vc6 ,这个目录为 Microsoft Visual Studio\VC98\MFC\SRC)
搜索含有"GetWindowMenuPopup failed!"字符串的文件,发现在该目录下
的WINMDI.CPP文件的第877行有这么一句
TRACE0("Warning: GetWindowMenuPopup failed!\n");
看来,这就是产生该警告的语句了。下面就来看看这条语句为什么被调用
备注:trace0是vc提供的一个宏,作用和printf类似,只是他的输出是在
vc的output窗口中。

三、函数流程分析
//Microsoft Visual Studio\VC98\MFC\SRC\WINMDI.CPP文件的851行
HMENU CMDIFrameWnd::GetWindowMenuPopup(HMENU hMenuBar)
// find which popup is the "Window" menu
{
if (hMenuBar == NULL)
return NULL;

ASSERT(::IsMenu(hMenuBar));

int iItem = ::GetMenuItemCount(hMenuBar);
while (iItem--)
{
HMENU hMenuPop = ::GetSubMenu(hMenuBar, iItem);
if (hMenuPop != NULL)
{
int iItemMax = ::GetMenuItemCount(hMenuPop);
for (int iItemPop = 0; iItemPop < iItemMax; iItemPop++)
{
UINT nID = GetMenuItemID(hMenuPop, iItemPop);
if (nID >= AFX_IDM_WINDOW_FIRST && nID <= AFX_IDM_WINDOW_LAST)
return hMenuPop;
}
}
}

// no default menu found
TRACE0("Warning: GetWindowMenuPopup failed!\n");
return NULL;
}

我们来看看GetWindowMenuPopup函数的流程,它以一个菜单句柄hMenuBar作为输入参数,
然后依次查看这个菜单的各子菜单,在每个子菜单里依次查看其菜单项的id,如果某个菜单项
的id在AFX_IDM_WINDOW_FIRST和AFX_IDM_WINDOW_LAST之间,就返回该菜单项所在的子菜单句柄。
如果没找到,就执行后面的trace0语句,输出开头提到的
"Warning: GetWindowMenuPopup failed!"
最后返回一个null值。
现在我们找到了初步的原因-该项目中所有的菜单项的id都不在AFX_IDM_WINDOW_FIRST和
AFX_IDM_WINDOW_LAST之间,因此函数不会在循环体内结束。如果想去掉这个警告,只要手工
修改项目中的resource.h文件,使某个菜单项的id值满足代码中要求的条件即可。再次编译运行,
这个警告果然没有了。
那么AFX_IDM_WINDOW_FIRST和AFX_IDM_WINDOW_LAST的值究竟是多少呢??
在Microsoft Visual Studio\VC98\MFC\Include\AFXRES.H的219行可以看到
#define AFX_IDM_WINDOW_FIRST 0xE130
#define AFX_IDM_WINDOW_LAST 0xE13F
那么这两个id到底代表什么呢?

四、对比实验——查看mdi程序的默认设置
为了更清楚的看明各id的含义,我们在vc6中建立一个默认的mdi程序,名为mditest.
在return hMenuPop;一句前加上断点,看看满足条件的id到底是多少。
经过实际的调试,这个nID为十六进制的0xe130,
而它对应的菜单项id为ID_WINDOW_NEW,即afxres.h文件的第212行
#define ID_WINDOW_NEW 0xE130
而这个菜单项正是mditest程序中的IDR_MDITESTYPE菜单中的第四个子菜单
的第一个菜单项,即"窗口->新建窗口"菜单。也就是说mfc默认把"窗口"子菜单做为
GetWindowMenuPopup返回的弹出菜单。

五、小结
本文中的警告产生流程可以总结如下
1、在mfc程序的CMDITestApp::InitInstance函数中,有一句
pDocTemplate = new CMultiDocTemplate(
IDR_MDITESTYPE,....);
第一个参数的id值有很多含义,其中一个就是程序主菜单的id,程序通过这个id
得到主菜单的句柄,把它作为参数传给CMDIFrameWnd::GetWindowMenuPopup
函数。
2、CMDIFrameWnd::GetWindowMenuPopup的第一次调用发生在
if (!ProcessShellCommand(cmdInfo))一句中,
GetWindowMenuPopup的实际作用就是从输入的父菜单中抽出一个可以作为弹出菜单的子菜单。
不过这个弹出菜单如何在程序运行时被显示出来,我还没找到办法,希望有知道的读者告诉我。


六、扩充实验
在MDITest程序的资源视图中,删掉IDR_MDITESTYPE菜单,弹出菜单的警告没有了
又会出现另外一个警告
Warning: no shared menu for document template #129.
Warning: no shared menu for document template #129.
有的兴趣读者可以自行分析以上现象产生产生的原因,并把您的心得和我交流。
...全文
297 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
ringphone 2007-10-31
  • 打赏
  • 举报
回复
MDI窗口创建时需要创建MDICLIENT窗口,即所有MDI子窗口的包容窗口,而创建这个MDICLIENT窗口需要一个CLIENTCREATESTRUCT结构作为初始参数传入,需要指定这个结构的hWindowMenu为“窗口”子菜单,idFirstChild为菜单ID,这个ID就是第一个子窗口在“窗口”菜单下的菜单ID
daidongsheng 2007-10-29
  • 打赏
  • 举报
回复
0分
wshcdr 2007-10-27
  • 打赏
  • 举报
回复
Up
sTigerwsk 2007-10-27
  • 打赏
  • 举报
回复
为啥又是0分贴呢?

R9R9R9 2007-10-27
  • 打赏
  • 举报
回复
up laomai
Dreams2007 2007-10-27
  • 打赏
  • 举报
回复
bs 0分贴
Dreams2007 2007-10-27
  • 打赏
  • 举报
回复
UP
lidongri 2007-10-27
  • 打赏
  • 举报
回复
bs 0分贴
liuxiuk 2007-10-27
  • 打赏
  • 举报
回复
强..!

bobob 2007-10-27
  • 打赏
  • 举报
回复
置顶

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧