Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631956
  • 博文数量: 263
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 2555
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-26 11:20
文章分类

全部博文(263)

文章存档

2011年(10)

2010年(19)

2009年(170)

2008年(64)

我的朋友

分类: WINDOWS

2009-10-27 22:35:37

研究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;
}
 

 
CMDITestDoc有如下的定义,仅能从CRuntimeClass里面创建的。


class CMDITestDoc : public CDocument
{
protected: // 仅从序列化创建
     CMDITestDoc();               // 被保护的构造函数
     DECLARE_DYNCREATE(CMDITestDoc)             // 支持从 CRuntimeClass 信息中创建。
 

 
再接着进行CreateNewFrame。


CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
     // create a frame wired to the specified document
     CCreateContext context;           // 这个 CreateContext 传递到 LoadFrame 中
     context.m_pCurrentFrame = pOther;         // 此例中 = NULL
     context.m_pCurrentDoc = pDoc;              // = 刚才创建的文档
     context.m_pNewViewClass = m_pViewClass;   // 显示此文档的视类的类型
     context.m_pNewDocTemplate = this;
 
     if (m_pFrameClass == NULL)
          // 提示错误并返回
     // 利用 CRuntimeClass 信息创建框架窗口对象,此例中为 CChildFrame
     CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
 
     // 这里,我们又看到了 LoadFrame , 参考前面的 LoadFrame 吧
     // 在这里面,View 窗口也被产生出来。参考 TRACE 输出。
     pFrame->LoadFrame(m_nIDResource,
              WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles
              NULL, &context);
     return pFrame;
}
 

 
LoadFrame之后View窗口将被创建出来,接着进入到CMDITestDoc::OnNewDocument中,现在仅仅是一个空的函数,没有特定代码。


BOOL CMDITestDoc::OnNewDocument()
{
   TRACE("CMDITestDoc::OnNewDocument() entry\n");
     if (!CDocument::OnNewDocument())
         return FALSE;
 
     // TODO: 在此添加重新初始化代码
     // (SDI 文档将重用该文档)
 
     return TRUE;
}

 
最后是CDocTemplate::InitialUpdateFrame,这里面主要是激活新建的框架、文档、视,看得挺头疼的。


void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,
     BOOL bMakeVisible)
{
     // just delagate to implementation in CFrameWnd
     pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
}

 
现在,文档、框架窗口、视窗口全部被创建出来,我们胜利的返回到ProcessShellCommand处。显示和更新主窗口,完成了WinApp::InitInstance :


     // 主窗口已初始化,因此显示它并对其进行更新
     pMainFrame->ShowWindow(m_nCmdShow);
     pMainFrame->UpdateWindow();
 

 
 
看一下至此的TRACE输出,中间的DLL加载被去掉了:


Before CMultiDocTemplate
Before AddDocTemplate
Before new CMainFrame
CMainFrame::CMainFrame()
Before pMainFrame->LoadFrame
CMainFrame::PreCreateWindow entry         // 注意:PreCreateWindow 被两次调用
CMainFrame::PreCreateWindow entry
CMainFrame::OnCreate entry before CMDIFrameWnd::OnCreate
CMainFrame::OnCreate before m_wndToolBar.CreateEx
CMainFrame::OnCreate before m_wndStatusBar.Create
Before ParseCommandLine
Before ProcessShellCommand
CMDITestDoc::CMDITestDoc()      // 文档对象被创建
CChildFrame::CChildFrame()      // 子框架窗口被创建
CChildFrame::PreCreateWindow entry
CChildFrame::PreCreateWindow entry
CChildFrame::PreCreateWindow entry
CMDITestView::CMDITestView() entry   // 子框架窗口的 OnCreate 中创建了 View 窗口
CMDITestView::PreCreateWindow entry
CMDITestDoc::OnNewDocument() entry
Before pMainFrame->ShowWindow
Before pMainFrame->UpdateWindow
 
// 退出时的 TRACE
CMDITestView::~CMDITestView()
CChildFrame::~CChildFrame()
CMDITestDoc::~CMDITestDoc()
CMainFrame::~CMainFrame()
阅读(885) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~