孙鑫VC第三课学习笔记
剖析基于MFC的应用程序
知识点一
MFC中是如何来对应传统的windows程序?
首先在CXXXApp中会定义一个全局变量 CXXXApp theApp
我们知道,在C/C++中,编译器会在进入main/WinMain函数之前会给全局变量分配内存空间,所以在MFC中,首先会调用theAPP,当然会进入CXXXApp类的构造函数CXXXApp()。
在VC++的安装目录下面,Microsoft Visual Studio/VC98/MFC/SRC中存放了MFC的部分源代码。其中在APPMODUL.cpp中包含了_tWinMain的一个宏函数,且
#define _tWinMain WinMain
其中在MFC的_tWinMain的宏函数中又调用了AfxWinMain函数。
这个AfxWinMain函数在WINMAIN.cpp中,其中调用了pThread->InitInstance(),但是由于InitInstance()是虚函数,根据多态性,调用的其实是派生类CXXXApp中的InitInstance()。
注册窗口类用了AfxEndDeferRefisterClass函数,在WINCORE.CPP中。当然,MFC已经根据不同的窗口类型,如ToolBar,StatusBar会默认指定窗口类型。在AfxEndDeferRefisterClass()真正调用了AfxRegisterClass(),进一步在AfxRegisterClass()调用了Win32的API函数::RegisterClass()函数。
接着就是产生窗口,窗口都是继承CWnd类,都有一个PreCreateWindow()函数,且都调用了CFrameWnd::PreCreateWindow,这个里面会有VERIFY(AfxDeferRefisterClass()),确认是否已经注册窗口类。这个AfxDeferRefisterClass其实就是宏函数,在AFXIMPL.H中有
#define AfxDeferRefisterClass(class) AfxEndDeferRefisterClass(class)
当然上面的注册窗口,在单文档程序中,是在调用派生类CXXXApp中的InitInstance()中的
ProcessShellCommand()中提前注册了。
创建窗口
CWnd::CreateEx函数(这个不是虚函数),这个函数被CFrameWnd::Create()调用。在CWnd::CreateEx又再一次调用了PreCreateWindow,且这个PreCreateWindow是虚函数,在这边设置这个函数的好处就是给用户一个机会在窗口产生之前可以修改窗口的类型。PreCreateWindow(CREATESTRUCT &cs),这边用了引用,所以在派生中的修改可以影响到基类。
下面就是在CXXXApp中的InitInstance()中的ShowWindow和UpdateWindow函数。
最后就是消息循环。
在CWinThread::Run(),在threadcore.cpp,这里面又调用了PumpMessage(),在这个函数的最后我们可以看到。
::TranslateMessgae()
::DispatchMessgae()
当然对于传统的回调函数,MFC中用了消息映射的机制,具体的消息由消息响应函数来处理。
知识点二
单文档程序如何将CXXXApp,CMainFrame,CXXXView,CXXXDoc组织起来?
数据的加载由文档类完成,数据的显示由View类完成。在CXXXApp的InitInstance()中利用文档模板组织起来。
CsingleDocTemplate* pDocTemplate;
pDocTemplate=new CsingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CXXXDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CXXXView));
AddDocTemplate(pDocTemplate);
知识点三
窗口类和窗口的关系。
MFC的类维护了窗口,一般的MFC类在析构函数中都会销毁窗口。所以,当MFC的类生命周期到的时候,窗口的生命周期就到了。但是,反过来,当窗口的生命周期到的时候,相应的类的生命周期就不一定了,类还是可以使用的。
知识点四
窗口显示的两种方法。
可以在窗口类型中加了WS_VISIBLE
CButton m_btn;
m_btn.Create(..,WS_VISIBLE,...)
或者
CButton m_btn;
m_btn.Create(..,,...)
m_btn.ShowWindow(SW_SHOWNORMAL);
知识点五
得到自己本身的指针用this。
得到父类的指针用GetParent()。