1. 程式人生 > >對mdi程式中一個彈出選單警告原因的分析

對mdi程式中一個彈出選單警告原因的分析

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

對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.
有的興趣讀者可以自行分析以上現象產生產生的原因,並把您的心得和我交流。

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述