1:用MFC生成的工程中都有一个名为CxxxApp的类,它派生于CWinApp类。这个类主要进行程序的初始化,生成文档、视图对象等工作。我们可以把需要全局访问的变量和函数定义为这个类的成员变量和成员函数,就可以实现全局访问了。
2:CwinApp类被称为应用程序对象。一个MFC程序只允许有一个应用程序对象
3:几个比较重要的类:c工程名app类处理消息,将接到的消息分给相应的对象;c工程名doc,c工程名view类C工程名View用来显示文档类C工程名Doc中的数据,并根据对视图类的操作修改文档类的数据。
4:#define __T(x) L ## x ;字符串前面加L表示该字符串是Unicode字符串。_T是一个宏,如果项目使用了Unicode字符集(定义了UNICODE宏),则自动在字符串前面加上L,否则字符串不变。因此,Visual C++里边定义字符串的时候,用_T来保证兼容性。VC支持ascii和unicode两种字符类型,用_T可以保证从ascii编码类型转换到unicode编码类型的时候,程序不需要修改。如 L"我的字符串" 表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。_T宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的(Unicode还是ANSI)字符处理方式。如果你定义了UNICODE,那么_T宏会把字符串前面加一个L。这时 _T("ABCD") 相当于 L"ABCD" ,这是宽字符串。TEXT,_TEXT 和_T 一样的。
5:typedef __w64 int INT_PTR, *PINT_PTR; __w64 在64位编译器下,而且打开了/Wp64编辑选项时,编辑器会对使用了__w64的类型进行32位到64位移植性的判断,比如讲将64位的指针赋值给INT_PTR编译器会发出警告,所以原式可写为:
typedef int INT_PTR, *PINT_PTR; 用INT_PTR表示 int, 用PINT_PTR表示 int*
6:CWnd类在头文件afxwin.h中,是MFC窗口的基类,提供了微软基础类库中所有窗口类的基本功能。m_hwnd 指明这个cwmd对象相关联的HWND句柄,CWnd对象与窗口想关联时m_hWnd句柄有值。
7:弹出对话框比较关键的一个函数,就是对话框类的DoModal()函数。 CDialog::DoModal()函数的原型为:virtual INT_PTR DoModal(); 返回值:整数值,指定了传递给CDialog::EndDialog(该函数用于关闭对话框)的nResult参数值。如果函数不能创建对话框,则返回-1;如果出现其它错误,则返回IDABORT。调用了它对话框就会弹出,返回值是退出对话框时所点的按钮的ID,比如,我们点了“退出”按钮,那么DoModal返回值为IDCANCEL。
8:理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。
9:一般下划线开头的东西是编译器和标准库内部使用的名字,下划线开头的函数一般都是专用的函数,一般用于特定系统相关。一般只有以经广泛使用的系统库函数和宏才有资格使用下划线开头(为了移植性一般不使用)
10:面向对象程序设计思想的核心是将编程过程中操作的数据作为对象处理
11:MFC程序执行过程剖析
1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用程序的窗口函数。而在MFC程序当中我们不在能找到类似WinMain这样的程序入口,取而代之的是一系列派生类的声明和定义以及一个冲CWinApp类派生而来的类的全局对象。CWinApp类被称之为应用程序对象,在一个MFC程序当中只允许有一个应用程序对象。由于CWinApp的派生对象是全局的,因此这个对象的构造函数会在所有的其他代码运行之前被调用,而由于CWinApp类当中包含了HWND、HINSTANCE等句柄的存在,其构造函数就执行了对这些成员数据的初始化操作,这里的所谓初始化仅仅是把所有的句柄对象赋值为NULL。
2)在调用完CWinApp的构造函数以后由连接器向程序内自动链接的AfxWinMain函数将被调用,而这个函数可以被看作MFC程序的入口函数。在这个函数当中调用全局AfxGetApp()函数获得应用程序对象,这时将调用AfxInit全局函数,这个函数的功能是使用操作系统传递给AfxWinMain函数的参数初始化应用程序对象当中的相关句柄数据成员。
3)之后AfxWinMain函数调用CWinApp::InitApplication成员函数,这个成员函数用来初始化应用程序对象当中的关于文档部分的内容。
4)随后调用CWinApp::InitInstance成员函数,在这个成员函数当中,使用new操作在堆上声明一k1个框架窗口对象,由此导致框架窗口对象的构造函数被调用,在框架窗口构造函数当中调用Create函数来创建窗口,而调用的Create函数一般将WNDCLASS参数设置成NULL,这样就由MFC内部调用PreCreateWindow函数,在这个函数当中由MFC注册几个默认的WNDCLASS供框架窗口的Create使用。这时程序控制权交还给CWinApp::InitInstance成员函数内部,由这个函数调用CWnd::ShowWindow显示窗口并且调用CWnd::UpdateWindow向窗口发送WM_PAINT消息。调用完CWinApp::InitInstance成员函数后由AfxWinMain函数调用CWinApp::Run成员函数,并由这个函数来创建和处理消息循环,并且在没有消息的时候处理OnIdle空闲处理。至此整个程序的创建过程完成。
5)在程序的运行过程当中,由操作系统源源不断的发送消息给应用程序,并且由CWinApp::Run当中的消息循环处理并且分发给相关的窗口对象的DefWindowProc成员函数,并由这个成员函数查询窗口对象的消息映射表,如果查到对应项,则由登记在消息映射表当中的类成员函数处理,否则则按照Message Route当中的顺序象父层类发送。
6)在消息运行结束,用户按下关闭按钮后,操作系统向程序发送WM_CLOSE消息,默认状况下程序调用DestoryWindow并且发送WM_DESTORY消息,应用程序接受到这个消息以后的默认操作是调用PostQuitMessage函数,由这个函数发送WM_QUIT消息。当程序对象接受到WM_QUIT消息后消息循环结束,由AfxWinMain函数调用AfxTerm函数清理程序使用过的资源并且结束整个程序。
小结:以上的所有描述涵盖了一个程序从开始、运行到结束的所有过程。 相信大家有点晕点了吧,实际编程中没有必要深刻理解这么多,这些大都是由MFC内部自动帮我们完成的。实际MFC编程过程中,其实懂得MFC程序中各个函数的执行流程即可。有时候过于追究MFC细节会白白浪费我们的精力,应该将主要精力放在使用MFC解决实际问题上。
MFC程序流程:winmain()函数调用应用程序对象(CWinApp)的InitApplication()和InitInstance()成员函数初始化应用程序,调用Run()成员函数运行应用程序的消息循环调用应用程序对象的ExitInstance()的成员函数退出应用程序
12:VC6中SDI(单文档界面)程序的执行流程
下面以VC6中的sdi工程为例,通过给每个函数前设置断点后调式执行,可以看出MFC的SDI的执行流程。记录如下,希望对MFC执行有疑惑的人有帮助。
1)CSdiApp theApp; //sdi.cpp
2)CSdiApp::CSdiApp() //sdi.cpp
3)BOOL CSdiApp::InitInstance() //sdi.cpp
4)CSdiDoc::CSdiDoc() //sdiDoc.cpp
5)CMainFrame::CMainFrame() //MainFrm.cpp
6)BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) //MainFrm.cpp
7)int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) //MainFrm.cpp
8)CSdiView::CSdiView() //sdiView.cpp
9)BOOL CSdiView::PreCreateWindow(CREATESTRUCT& cs) //sdiView.cpp
10)BOOL CSdiDoc::OnNewDocument() //sdiDoc.cpp
11) void CSdiView::OnDraw(CDC* pDC) //sdiView.cpp
// ---------------- 关闭窗口后-------------------------------------
12) CSdiView::~CSdiView()
13) CMainFrame::~CMainFrame()
14) CSdiDoc::~CSdiDoc()
13:windows应用程序是事件驱动的,一般不会显式的调用函数获取输入,而是等待系统将接收到的输入传递给应用程序。系统会将应用程序的输入传递给不同的窗口。每一个窗口有一个对应函数,称为窗口函数,当有对应窗口的输入时,系统会调用此函数。窗口函数会处理输入,并执行对系统的控制。系统使用消息的形式将输入传递给窗口函数。以WM开头的为消息PostMessage()函数将消息发送到消息队列(鼠标键盘的输入一般使用这种形式)。SendMessage()函数将消息直接发送给窗口大部分指针类型的数据类型都是以P或者LP为前缀 。
14:WinMain()函数的主要完成三个工作:注册窗体类(使用RegisterClassEx函数,在系统中注册对应的窗体类)、创建窗口(使用CreateWindow函数创建窗口后调用ShowWindow函数显式窗口)和启动消息循环。
15:Win32程序主体,通过while语句实现消息循环:
WinMain(...)
{ MSG msg; //定义消息
RegisterClass(...); //注册窗口类
CreateWindow(...); //创建窗口
UpdateWindow(...);
Whle(GetMessage(&msg,...))消息循环
{
TranslateMessage(...);//消息打包
DispatchMessage(...);//消息分发
}
}
WinMain函数被封装到了CWinApp类中,消息循环由CWinApp::Run()函数取代,窗口过程WndProc则被封装到CWnd类的WindowProc()函数中,消息通过消息映射机制传递到相应的处理函数。
.h文件中
DECLARE_MESSAGE_MAP声明了两个数据结构AFX_MSGMAP_ENTRY和AFX_MSGMAP。AFX_MSGMAP_ENTRY结构包含了一个消息的所有相关消息。AFX_MSGMAP主要有两个作用 1:用来得到基类的消息映射入口地址。2:得到本身的消息映射入口地址。
.cpp中
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)
//{{AFX_MSG_MAP(CInheritClass)
ON_COMMAND(ID_EDIT_COPY,OnEditCopy)
……
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
这里主要进行消息映射的实现,把它和消息处理函数联系在一起。其中出现三个宏,第一个宏是BEGIN_MESSAGE_MAP有两个参数,分别是拥有消息表格的类及其父类 。第二个宏是ON_COMMAND, 指定命令消息的处理函数名称,把各种各样的消息与特定的处理函数关联起来。第三个宏是END_MESSAGE_MAP作为结尾符号。MFC通过这三个宏把所有的消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有的消息和与它们相关的参数。同时通过AFX_MSGMAP能得到该数组的首地址,同时得到基类的消息映射入口地址。这样,由DECLARE_MESSAGE_MAP定义,由BEGIN_MESSAGE_MAP、ON_COMMAND以及END_MESSAGE_MAP三个宏实现的一个消息映射表就形成了。
MFC 应用程序框架结构的基石是文档/视图结构,它定义了一种程序结构, 这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据。MFC 在 CDocument 和 CView 中为文档和视图提供基础结构
CWinApp是CWinThread类的派生类,封装了基于MFC的 Windows 应用程序的初始化、 运行及终止等功能。 CWinApp 的全局对象控制着整个应用程序的流程。
16:初始化代码在OnInitDialog
17:MFC程序流程
WinMain() ->AfxWinMain()(作用:1注册窗体类、2调用应用程序类的初始化函数InitInstance、调用应用程序类的Run()函数(实际上是父类的父类CWinThread的Run(函数)))
AfxWinMain函数由四个部分组成:AfxWinInit,InitApplication,InitInstance,Run
1:AfxWinInit()作用:给CWinApp类的主要的数据成员重新赋值并且调用AfxInitThread()为主线程做了一些初始化
2:InitApplication()作用:程序进行全局初始化
3:InitInstance()是一个虚函数,大多数应用程序都要override这个函数启动消息机制