|
尽管刚接触MFC不久,但其开发模式已初有感受,抛开复杂的应用不谈,想想我们通常怎样来写一个简单的MFC程序。也许浮现在你脑海里的是程序向导,类向导,资源编辑器,以及程序代码,此时你大概会灵光一现,说MFC程序=向导+资源+代码。看起来确实是这样,但有没有可能更简化一些呢,比如把向导去掉。想想是可以的,向导不过是简化MFC开发的一种手段,虽然没有类向导日子会很难过,但也使你有机会看看纯手工打造的MFC是怎么样的,这对于了解MFC的执行流程非常有帮助。 我试图从一个最简单的MFC程序开始,慢慢丰富它,使它最终成为一个有点用处的程序。这个过程中我会尽力抵挡住使用类向导的诱惑,只使用资源以及程序代码。当然可能需要好几篇文章的篇幅,因为这个程序是我计划好的,用来学习MFC的每一个技术点的实验品。现在,它的另一个目的是证明:MFC程序=资源+代码。 一个最简单的MFC程序 你一定有这样的经验,用MFC的程序向导生成的MFC代码满是宏,注释,条件定义,这些对于程序的健壮性很有必要,不过对于初学者却是可怕的怪兽。 其实MFC程序可以写得非常简单,只用一个头文件和一个源文件就可以了,用程序向导新建一个程序,选择Win32 Application类型的程序,我的工程名是:MiniDraw;在下一步选择“一个空工程”并完成掉它。 生成的工程仅仅是一个工作区,没有任何源文件,手工给它添加一个头文件和一个源文件,分别是:DrawApp.h; DrawApp.cpp。这是它们的代码: DrawApp.h: #ifndef LINZHENQUN_DRAWAPP_H_ #define LINZHENQUN_DRAWAPP_H_ class CDrawApp: public CWinApp { public: CDrawApp(); BOOL InitInstance(); 软件开发网 www.mscto.com }; #endif //LINZHENQUN_DRAWAPP_H_ DrawApp.cpp: #include <afxwin.h> #include "DrawApp.h" 软件开发网 www.mscto.com CDrawApp DrawApp; 软件开发网 www.mscto.cn CDrawApp::CDrawApp() 软件开发网 www.mscto.cn { } 软件开发网 www.mscto.com 软件开发网 www.mscto.cn BOOL CDrawApp::InitInstance() 软件开发网 www.mscto.cn { return FALSE; } 不管你信不信,这就是一个最简单的MFC程序,尽管它什么事情也做不了。在编译的时候会出现错误,这是因为没有添加MFC共享DLL,打开工程设置,在常规页将MFC下拉框选为“使用MFC作为共享DLL”。然后再编译看看,运行后它马上就返回了,我们得在InitInstance里面作点什么。 BOOL CDrawApp::InitInstance() { MessageBox(0, "Hello world", "", MB_OK); 软件开发网 www.mscto.cn return FALSE; } MFC版的Hello World完成了,可能你还觉得过于简陋,如果是这样的话,给程序加一个对话框怎么样呢。 对话框的界面通过资源来加,在新建向导的文件页里选“资源脚本”,输入文件名为resource,确定后便生成了resource.rc,并在IDE里可以见到它: 软件开发网 www.mscto.com 在resource.rc处点击右键菜单,选插入,在插入资源对话框里选Dialog,点新建后,一个对话框模板就生成了。 享受一下快速开发的感觉吧,接下来要将这个对话框资源与一个对话框类关联起来,使用类向导的话会非常简单,不过为了体现纯手工的意义,我决定还是用新建文件的方式。 新建AboutDialog.cpp和AboutDialog.h,代码如下: AboutDialog.h #ifndef LINZHENQUN_ABOUTDIALOG_H_ #define LINZHENQUN_ABOUTDIALOG_H_ class CAboutDialog: public CDialog { public: CAboutDialog(CWnd* pParent = NULL); 软件开发网 www.mscto.cn protected: virtual OnInitDialog(); 软件开发网 www.mscto.com DECLARE_MESSAGE_MAP(); }; 软件开发网 www.mscto.com #endif //LINZHENQUN_ABOUTDIALOG_H_ AboutDialog.cpp #include <afxwin.h> #include "AboutDialog.h" #include "resource.h" BEGIN_MESSAGE_MAP(CAboutDialog, CDialog) END_MESSAGE_MAP() 软件开发网 www.mscto.com CAboutDialog::CAboutDialog( CWnd* pParent /*== NULL*/ ): CDialog(IDD_ABOUTDLG, pParent) { } 软件开发网 www.mscto.com BOOL CAboutDialog::OnInitDialog() { return CDialog::OnInitDialog(); } 对话框资源的ID是IDD_ABOUTDLG,在resource.h里面声明,所以将这个头文件加进来,这个资源ID传给基类CDialog后,它会帮你处理对话框加载的事情,在上一篇文章提及过。 现在,回到DrawApp,将InitInstance改成这样: 软件开发网 www.mscto.cn BOOL CDrawApp::InitInstance() 软件开发网 www.mscto.com { CAboutDialog AboutDlg; AboutDlg.DoModal(); 软件开发网 www.mscto.com return FALSE; } 运行程序如下: 软件开发网 www.mscto.com 看看我们写的代码,总共只有4个文件,并且代码非常简单,学过SDK编程的你可能看出WinMain被CDrawApp::InitInstance()代替了,整个程序流程可以从这里开始,但是那些曾经熟悉的东西却不见了,比如WinMain呢,消息循环呢,毕竟好奇是人类的天性,而要发现答案,必须到框架里去看看。 这一切是怎样发生的 MFC与VCL有一个很大的区别,VCL刚开始用时觉得非常简单,你不需要理解SDK那一套东西,当你想一探究竟时,才发觉那里面的水很深;MFC则相反,一开始你会觉得摸不着头脑,这迫使你必须了解一点SDK的知识,当你理解了程序向导为你生成的这一堆代码之后,你会发现AFX的东西也变得一目了然。 程序的初始流程也是这样,CDrawApp声明了一个全局的类实例,它必须是一个单例,因为它代表着这个程序。而我们直接认为,在某个源文件里有一个WinMain函数,这个函数取得CDrawApp的类实例,然后调用CDrawApp::InitInstance(),然后调用CDrawApp::Run(); 就象下面这样: int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 软件开发网 www.mscto.com LPTSTR lpCmdLine, int nCmdShow) { int nReturnCode = -1; CWinApp* pApp = AfxGetApp(); If (pApp->InitInstance()) nReturnCode = pApp->Run(); return nReturnCode; } 实际的MFC代码与上面并没有相差太远,也许你又找回C++程序的那种顺序了。 当然作为一个框架,要考虑的东西远比上面的多,比如线程的信息,模块的信息,如何保存App单例等,实际的代码可以参考WINMAIN.CPP的AfxWinMain函数。 AfxGetApp()取回一个App指针,这个指针就是上面代码定义的DrawApp,CWinApp的构造函数将This指针存放在“全局的地方”,使得用AfxWinApp可以拿到。 我们的InitInstance()返回的是FALSE,因而Run不会执行到,直接就返回了,那么对话框的消息处理是怎么做到的呢,答案在上一篇文章中,就是对话框自己有一个消息循环叫RunModalLoop。 结合MFC实际的代码,概括起来就是下面几个点: 软件开发网 www.mscto.cn 1. 生成CDrawApp类实例,在构造函数中将This保存起来,让AfxGetApp可以拿到。 2. WinMain开始,拿到App类,初始化程序pApp->InitApplication(); 3. 初始化实例pThread->InitInstance(),pThread等于pApp。 4. 开始消息循环pThread->Run()。 5. WinMain返回,程序结束。 3、4步是整个程序的生命周期,InitInstance是创建窗口,Run是消息循环。用窗口表现界面,以事件驱动程序的模型就这样形成了。 上面所列出来的是经过简化的代码,我认为这也有好处,太关注分枝别叶只会让你头晕目眩,把这些东西摘除,只剩下干干净净的支杆,整个骨架就清清楚楚了,等到实际项目应用的时候,再慢慢丰富这些知识,这种由简到繁的学习方法一直为我所坚持。 软件开发网 www.mscto.cn
|