黑夜传说1百度云:学习MFC框架如何创建的过程.CWnd::ProcessShellCommand.上部

来源:百度文库 编辑:九乡新闻网 时间:2024/05/06 05:24:03
学习MFC框架如何创建的过程.CWnd::ProcessShellCommand.上部

系列快速导航:三要点

1、  CDocTemplate 学习MFC框架如何创建的过程.CDocTemplate

2、  CFrameWnd::LoadFrame 学习MFC框架如何创建的过程.LoadFrame

3、  CWnd::ProcessShellCommand
学习MFC框架如何创建的过程.CWnd::ProcessShellCommand.上部
学习MFC框架如何创建的过程.CWnd::ProcessShellCommand.下部


研究CWnd::ProcessShellCommand

 

第一个MDI子窗口是从这里面建立出来的,这实在是缺乏直观性。不过MFC就是这样,没办法。

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)

{

     BOOL bResult = TRUE;

     switch (rCmdInfo.m_nShellCommand)

     {

     case CCommandLineInfo::FileNew:

         if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))      // 关键是这里

              OnFileNew();

         if (m_pMainWnd == NULL)

              bResult = FALSE;

         break;

 

     case CCommandLineInfo::FileOpen:                // 忽略

     case CCommandLineInfo::FilePrintTo:            // 忽略

     case CCommandLineInfo::FilePrint:

     case CCommandLineInfo::FileDDE:

     case CCommandLineInfo::AppRegister:

     case CCommandLineInfo::AppUnregister:

     }

     return bResult;

}

进入到ProcessShellCommand,要处理很多种不同命令,我们忽略其它命令,单独看FileNew部分。

注意:实际进入到了AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)之中。

 

AfxGetApp()实际返回了CMDITestApp的唯一实例,它从CWinApp – CWinThread – CCmdTarget – CObject 派生而来。我们没有重载OnCmdMsg,所以进入到CCmdTarget的OnCmdMsg处理中。为了研究,我们删减了一些代码。

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,

     AFX_CMDHANDLERINFO* pHandlerInfo)

{

     // 这里删减了一些代码

     // determine the message number and code (packed into nCode)

     const AFX_MSGMAP* pMessageMap;

     const AFX_MSGMAP_ENTRY* lpEntry;

     UINT nMsg = 0;

     // 这里删减了一些代码,处理后 nMsg = WM_COMMAND

     // 为了简化,删减了一些断言等。以下循环用于查找处理此消息的入口。

     for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;

       pMessageMap = (*pMessageMap->pfnGetBaseMap)())

     {

         lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);

         if (lpEntry != NULL)

         {

              // 找到了消息处理项入口,分发此消息。

              return _AfxDispatchCmdMsg(this, nID, nCode,

                   lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);

         }

     }

     return FALSE;   // 未找到则不处理

}

最终MFC很愉快地找到了一个入口项,       CWinApp::OnFileNew(void)       要处理这个消息。继续进入到_AfxDispatchCmdMsg中去看看。

 

AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,

     AFX_PMSG pfn, void* pExtra, UINT_PTR nSig, AFX_CMDHANDLERINFO* pHandlerInfo)

         // return TRUE to stop routing

{

     union MessageMapFunctions mmf;

     mmf.pfn = pfn;

     BOOL bResult = TRUE; // default is ok

 

     if (pHandlerInfo != NULL)

     {

         // just fill in the information, don't do it

         pHandlerInfo->pTarget = pTarget;

         pHandlerInfo->pmf = mmf.pfn;

         return TRUE;

     }

 

     switch (nSig)

     {

     case AfxSigCmd_v:

         // normal command or control notification

         ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED

         ASSERT(pExtra == NULL);

         (pTarget->*mmf.pfnCmd_v_v)();         // ? 实际调用 pTarget 指向的这个成员函数

         break;

     // 下面还有大量的多种 AfxSigCmd_xxx,忽略掉它们。

     default:    // illegal

         ASSERT(FALSE); return 0; break;

     }

     return bResult;

}

 

其中 (pTarget->*mmf.pfn_Cmd_v_v)() 对CWinApp::OnFileNew() 产生调用,pTarget = CMDITestApp类实例。调用进入如下:

 

void CWinApp::OnFileNew()

{

     if (m_pDocManager != NULL)

         m_pDocManager->OnFileNew();

}

 

进入进入到CDocManager::OnFileNew()

 

void CDocManager::OnFileNew()

{

     if (m_templateList.IsEmpty())

          // 提示没有模板并返回

     CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();    // 第一个

     if (m_templateList.GetCount() > 1)

          // 弹出一个对话框(很难看的)提示用户选择一个文档模板

 

     // 在这个例子里面,pTemplate 就是 CMDITestApp::InitInstance() 里面创建的那个模板

     pTemplate->OpenDocumentFile(NULL);

}

 

在进入CMultiDocTemplate::OpenDocumentFile之前,我观察了一下调用堆栈,结果如下:

>   mfc71d.dll!CDocManager::OnFileNew()  行852  C++

    mfc71d.dll!CWinApp::OnFileNew()  行25   C++

    mfc71d.dll!_AfxDispatchCmdMsg(CCmdTarget * pTarget=0x0042cae8, unsigned int nID=57600, int nCode=0, void (void)* pfn=0x0041153c, void * pExtra=0x00000000, unsigned int nSig=53, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  行89   C++

    mfc71d.dll!CCmdTarget::OnCmdMsg(unsigned int nID=57600, int nCode=0, void * pExtra=0x00000000, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  行396 + 0x27    C++

    mfc71d.dll!CWinApp::ProcessShellCommand(CCommandLineInfo & rCmdInfo={...})  行27 + 0x1e C++

    MDITest.exe!CMDITestApp::InitInstance()  行101 + 0xc    C++

希望我还没有迷路:)

 

 

CMultiDocTemplate::OpenDocumentFile 又是很多很多代码,让我们选择一些。

CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,

     BOOL bMakeVisible)

{

     // 以下代码删减了验证、断言部分

     CDocument* pDocument = CreateNewDocument();              // 创建文档对象

     CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);    // 创建框架窗口

 

     if (lpszPathName == NULL)

     {

         pDocument->OnNewDocument();           // 初始化文档

     }

     else

          // 打开已有文档

 

     InitialUpdateFrame(pFrame, pDocument, bMakeVisible);

     return pDocument;

}

 

 

看一看CreateNewDocument()

CDocument* CDocTemplate::CreateNewDocument()

{

     // default implementation constructs one from CRuntimeClass

     if (m_pDocClass == NULL)

          // 错误提示啦

     // CRuntimeClass* m_pDocClass -> CreateObject 实例化文档类。

     // 在此例子中既是 CMDITestDoc

     CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();

     AddDocument(pDocument);      // 添加到模板里的文档列表,MultiDocTemplate 保存此一文档

     return pDocument;

}