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

对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.
有的兴趣读者可以自行分析以上现象产生产生的原因,并把您的心得和我交流。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用 Java 编写股票 DMI 指标的 ADX、PDI 和 MDI 计算的代码: ```java public class DMIIndicator { // 计算ADX public static double[] adx(double[] high, double[] low, double[] close, int period) { double[] tr = new double[high.length]; double[] atr = new double[high.length]; double[] pdi = new double[high.length]; double[] mdi = new double[high.length]; double[] dx = new double[high.length]; double[] adx = new double[high.length]; // 计算TR和ATR for (int i = 1; i < high.length; i++) { double h_l = high[i] - low[i]; double h_pc = Math.abs(high[i] - close[i - 1]); double l_pc = Math.abs(low[i] - close[i - 1]); tr[i] = Math.max(h_l, Math.max(h_pc, l_pc)); atr[i] = ((period - 1) * atr[i - 1] + tr[i]) / period; } // 计算PDI和MDI for (int i = period + 1; i < high.length; i++) { double pdm = high[i] - high[i - 1]; double mdm = low[i - 1] - low[i]; pdi[i] = 100 * ((period - 1) * pdi[i - 1] + pdm) / (period * atr[i]); mdi[i] = 100 * ((period - 1) * mdi[i - 1] + mdm) / (period * atr[i]); } // 计算DX for (int i = 2 * period; i < high.length; i++) { double di_diff = Math.abs(pdi[i] - mdi[i]); double di_sum = pdi[i] + mdi[i]; dx[i] = 100 * (period * dx[i - 1] + di_diff / di_sum) / (period + 1); } // 计算ADX for (int i = 2 * period; i < high.length; i++) { adx[i] = ((period - 1) * adx[i - 1] + dx[i]) / period; } return adx; } // 计算PDI public static double[] pdi(double[] high, double[] low, double[] close, int period) { double[] tr = new double[high.length]; double[] atr = new double[high.length]; double[] pdi = new double[high.length]; // 计算TR和ATR for (int i = 1; i < high.length; i++) { double h_l = high[i] - low[i]; double h_pc = Math.abs(high[i] - close[i - 1]); double l_pc = Math.abs(low[i] - close[i - 1]); tr[i] = Math.max(h_l, Math.max(h_pc, l_pc)); atr[i] = ((period - 1) * atr[i - 1] + tr[i]) / period; } // 计算PDI for (int i = period + 1; i < high.length; i++) { double pdm = high[i] - high[i - 1]; pdi[i] = 100 * ((period - 1) * pdi[i - 1] + pdm) / (period * atr[i]); } return pdi; } // 计算MDI public static double[] mdi(double[] high, double[] low, double[] close, int period) { double[] tr = new double[high.length]; double[] atr = new double[high.length]; double[] mdi = new double[high.length]; // 计算TR和ATR for (int i = 1; i < high.length; i++) { double h_l = high[i] - low[i]; double h_pc = Math.abs(high[i] - close[i - 1]); double l_pc = Math.abs(low[i] - close[i - 1]); tr[i] = Math.max(h_l, Math.max(h_pc, l_pc)); atr[i] = ((period - 1) * atr[i - 1] + tr[i]) / period; } // 计算MDI for (int i = period + 1; i < high.length; i++) { double mdm = low[i - 1] - low[i]; mdi[i] = 100 * ((period - 1) * mdi[i - 1] + mdm) / (period * atr[i]); } return mdi; } } ``` 使用方法: ```java double[] high = {10.0, 11.0, 12.0, 13.0, 14.0}; double[] low = {9.0, 10.0, 11.0, 12.0, 13.0}; double[] close = {9.5, 10.5, 11.5, 12.5, 13.5}; int period = 14; double[] adx = DMIIndicator.adx(high, low, close, period); double[] pdi = DMIIndicator.pdi(high, low, close, period); double[] mdi = DMIIndicator.mdi(high, low, close, period); ``` 其,`high`、`low`、`close` 分别是股票的最高价、最低价和收盘价,`period` 是计算周期。`adx`、`pdi` 和 `mdi` 分别是计算出来的 ADX、PDI 和 MDI 数组。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值