1.先看流程:
BOOL CtestDlgApp::InitInstance()
{
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CtestDlgDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CtestDlgView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准外壳命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo)) //关键:一直到这个时候,m_pMainWnd还是0X0000000000,该句执行完以后,m_pMainWnd分配了内存空间
return FALSE;
CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
CView *pView=pFrame->GetActiveView();
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
}
2、那么 if (!ProcessShellCommand(cmdInfo)) 做了什么操作呢?
BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
{
BOOL bResult = TRUE;
switch (rCmdInfo.m_nShellCommand)
{
case CCommandLineInfo::FileNew:
if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) 关键:在此 生成了m_pMainWnd,同时,也生成了view,doc?
OnFileNew();
if (m_pMainWnd == NULL)
bResult = FALSE;
break;
......
.......
上面这一步是为什么呢?
(关于OnCmdMsg:搜索一下很多类都重载了OnCmdMag,这有什么奥秘呢?
OnCmdMsg虚拟函数在MFC命令消息的发送中扮演了重要的角色,CFrameWnd的OnCmdMsg实现了MFC的标准命令消息发送路径。
那么,就产生一个问题:如果命令消息不送给边框窗口对象,那么就不会有按标准命令发送路径发送消息的过程?答案是肯定的。例如一个菜单被一个对话框窗口所拥有,那么,菜单命令将送给MFC对话框窗口对象处理,而不是MFC边框窗口处理,当然不会和CFrameWnd的处理流程相同。
但是,有一点需要指出,一般标准的SDI和MDI应用程序,只有主边框窗口拥有菜单和工具条等用户接口对象,只有在用户与用户接口对象进行交互时,才产生命令,产生的命令必然是送给SDI或者MDI程序的主边框窗口对象处理。
)OnCmdMsg OnWndMsg 有什么关系?有什么区别?如下:
AfxWndProc()
接受消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc()
AfxCallWndProc()
保存消息(消息标识符和参数)供以后参考,然后调用WindowProc()
WindowProc()
发送消息到OnWndMsg(),如果未被处理,则调用DefWindowProc()
代码如下
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
OnWndMsg()
按字节对消息进行排序,对于WM_COMMAND消息,调用OnCommand();
对于WM_NOTIFY消息,调用OnNotify(),OnWndMsg()搜索类的消息映像,
以找到一个能处理消息的处理函数,如果OnWndMsg()不能找到这样一个函数,则将消息返回给WindodProc(),由它将消息发到DefWindowProc()
在CWnd中,MFC使用OnWndMsg来分别处理各类消息:
如果是WM_COMMAND消息,交给OnCommand处理;然后返回。
如果是WM_NOTIFY消息,交给OnNotify处理;然后返回。
如果是WM_ACTIVATE消息,先交给_AfxHandleActivate处理(后面5.3.3.7节会解释它的处理),再继续下面的处理。
如果是WM_SETCURSOR消息,先交给_AfxHandleSetCursor处理;然后返回。
如果是其他的Windows消息(包括WM_ACTIVATE),则
首先在消息缓冲池进行消息匹配,
若匹配成功,则调用相应的消息处理函数;
若不成功,则在消息目标的消息映射数组中进行查找匹配,看它是否处理当前消息。这里,消息目标即CTview对象。
如果消息目标处理了该消息,则会匹配到消息处理函数,调用它进行处理;
否则,该消息没有被应用程序处理,OnWndMsg返回FALSE。
OnCommand()
Oncommand()查看这是不是一个控件通知(lParam是不是空的),如果它是,OnCommand()试图将消息映射到制造通知的控件,如果它不是则调用OnWndMsg().
OnNotify()
OnNotify()也试图将消息映射到制造通知的控件,如果映射不成功,则调用OnWndMsg()返回。
OnCmdMsg()
根据接受消息的类,OnCmdMsg()将在一个称为命令传递的过程中潜在的传递命令消息和控件通知。如果该窗口的类是一个框架类,则命令消息和通知也被映射到视图和文档类中,并为该类寻找消息处理函数。
3、消息处理几乎所有的窗口对象使用AfxWndProc()窗口处理函数
(1)通过一个包含了窗口句柄和对象指针等信息的列表获取指向对象的指针
(2)通过此指针调用这个指针指向的CWND的虚函数WindowProc()
(3)WindowProc()调用OnWndMsg()对消息、命令和通知进行处理,对于消息OnWndMsg()通过DECLARE_MESSAGE_MAP等消息映射宏完成
①选取菜单产生的消息叫命令,控件产生的消息是通知
②命令处理流程是:CWnd::onwndmsg()->CFrameWnd::oncommand()->细节检查->CWnd::OnCommand->CFrameWnd::oncmdmsg()
消息处理流程是:onwndmsg()->onnotify()->细节检查->oncmdmsg()
③oncmdmsg()做为虚函数可以对命令和消息有不同的实现
最后,请注意OnCmdMsg()
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
CPushRoutingFrame push(this);
// pump through current view FIRST
CView* pView = GetActiveView();
if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
很明显,CView,CWinApp,CWnd都实现了OnCmdMsg(),甚至Cdocument也实现了。
另外,CFrameWnd虽然也覆盖了OnCmdMsg(),但它只是做了分发工作。告诉它们到底是该谁调用OnCmdMsg了