MFC框架程序剖析


MFC程序的运行过程:


(1)首先利用全局应用程序对象theApp启动应用程序。正是产生了这个全局对象,基类CWinApp中的this指针才能指向这个对象。如果没有这个全局对象,程序在编译时不会出错,但在运行时就会出错。


(2)调用全局应用程序对象的构造函数,从而就会先调用其基类CWinApp的构造函数。后者完成应用程序的一些初始化工作,并将应用程序对象的指针保存起来。


(3)进入WinMain函数。在AfxWinMain函数中可以获取子类(对Test程序来说,就是CTestApp类)的指针,利用此指针调用虚函数:InitInstance,根据多态性原理,实际上调用的是子类(CTestApp)的InitInstance函数。后者完成应用程序的一些初始化工作,包括窗口类的注册、创建,窗口的显示和更新。期间会多次调用CreateEx函数,因为一个单文档MFC应用程序有多个窗口,包括框架窗口、工具条、状态条等。


(4)进入消息循环。虽然也设置了默认的窗口过程函数,但是MFC应用程序实际上是采用消息映射机制来处理各种消息的。当收到WM_QUIT消息时,退出消息循环,程序结束。

面已经知道,在一个windows程序中,winmain函数作为程序的入口函数,一个程序的是:设计窗口(给窗口设置相应的属性)-->注册窗口-->创建窗口-->显示并且更新窗口-->消息循环;

  但是当创建一个MFC程序时候,并没有看到winmain函数之类的东西,并且之前的程序框架与MFC也大有不同。现在,先来看看MFC中的程序框架以及程序运行机制吧。

  由于MFC是对其SDK的封装,所以程序运行逻辑方式应该是一样的。

  在MFC程序中,同样还是有winmain函数参与,但是这个WinMain函数是在程序编译连接时,由链接器将该函数链接到程序的。知道WinMain的存在后,那么WinMain又是怎么跟其他类组织在一起的呢?

  (1)CxxxApp中,有一个theApp对象的全局对象,该对象就表示了应用程序本身。对于全局对象theApp,它是在程序进入WinMain函数之前为其分配的空间,同时,定义其时候会调用它的构造函数。在程序中看到CxxxApp是CWinApp的子类,当调用CxxxApp的构造函数之前,会调用其父类的构造函数。程序的初始化在CWinApp构造函数完成。

  (2)AfxWinMain函数:WinMain函数实际上是通过调用AfxWinMain函数来完成其功能的。AfxWinMain源代码在MFC源代码的WINMAIN.CPP文件中。AfxWinMain首先通过调用AfxGetThread函数获得一个CWinThread类型的指针,接着调用AfxGetApp函数获得一个CWinApp类型的指针。由于CWinApp继承自CWinThread,AfxGetThread函数实际上返回的是AfxGetApp函数的结果,因此上面所说的AfxGetThread函数获得一个CWinThread类型的指针,接着调用AfxGetApp函数获得一个CWinApp类型的指针所得到的两个指针实际上是一致的。都指向theApp全局对象。

  (3)InitInstance函数:接下来pThread和pApp调用了三个函数(在AfxWinMain所在的文件WINMAIN.CPP中可以看到)。分别是pApp->InitApplication();pThread->InitInstance();pThread->Run();三个函数。这三个函数完成了程序所需要的几个步骤:设计窗口类,注册窗口类,显示窗口类,消息循环,以及窗口过程函数。(通过进一步的跟进每个函数可以发现这三个函数最终需要调用跟win32中程序完成这些功能的函数的影子!)

  设计和注册窗口:MFC已经为我们预定义了一些默认的标准窗口类,而注册窗口类是由AfxEndDeferRegisterClass函数完成。(AfxEndDeferRegisterClass函数首先获得窗口类的信息,如果该窗口类已经注册,直接返回一个真值,否则,调用RegisterClass函数注册该窗口类。其与win32SDK中所使用的函数是一样的啊!)

  创建窗口:窗口的创建是由CWnd类中的CreateEx函数实现的,在MFC中,CFrameWnd类的Create函数内部调用了上述的CreateEx函数,而前者又是由CrameWnd中的LoadFrame函数调用!!!在CreateEx函数的实现过程中,调用了PreCreateWindow,而后者是一个虚函数,故此调用是调用子类的PreCreateWindow函数,之所以这样是在窗口产生之前让程序员能够有机会修改窗口的外观。参数类型是一个CREATESTRUCT结构体。

  显示与更新窗口:CxxxApp类中有一个m_pMainWnd的成员变量,是一个CWnd类型的指针,保存了应用程序窗口的指针。通过它调用与SDK中一样的ShowWindow与UpdateWindow函数完成任务。

  消息循环:前面已经提到过pThread->Run();这个函数就是实现了消息循环。在这个函数中的for循环中,执行了我们认识的TransLateMessage与DisPatchMessage两个函数;

  窗口过程:在AfxEndDeferRegisterClass函数的源程序中,其中有一行言谢眼熟的代码:wndcls.lpfnwndProc=DefWindowProc;当然MFC程序并不是把所有的消息都交给DefWindowProc窗口过程来处理。MFC中使用的是消息映射机制!

  一个MFC消息响应函数在程序中有三处相关的信息:函数原型,函数实现,用来关联消息和消息响应函数的宏。(分别分布在头文件及源文件中)

  头文件中在两个AFX_MSG 注释宏之间是消息响应函数的原型的声明。源文件中有两处:

  (1)AFX_MSG_MAP注释宏之间的消息映射宏,通过这个宏把消息与消息响应函数关联起来;

  (2)源文件中消息响应函数的实现代码。

  MFC中的消息映射机制的实现方法:在每个能接收和处理消息的类中,定义一个消息和消息的处理函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针成对出现。某个类能处理的所有的消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否函数该消息,就可以知道该内能够处理此消息。如果能,则依照静态表找到对应的消息处理函数。否则,将消息传给其父类。

;

}

nReturnCode = pThread->Run();

InitFailure:

#ifdef _DEBUG

// Check for missing AfxLockTempMap calls

if (AfxGetModuleThreadState()->m_nTempMapLock != 0)

{

TRACE1("Warning: Temp map lock count non-zero (%ld).\n",

AfxGetModuleThreadState()->m_nTempMapLock);

}

AfxLockTempMaps();

AfxUnlockTempMaps(-1);

#endif

AfxWinTerm();

return nReturnCode;

}

pThreadpApp所指向的都是CTestApp类的对象,即theApp全局对象。

上面代码中,pThreadpApp调用了三个函数。

pApp首先调用InitApplication函数,该函数完成MFC内部管理方面的工作。接着,调用pThreadInitInstance函数。最后pThread调用RUN()。

run()如果返回特定值,则程序结束,也就是winmain结束。

第三步

猜你喜欢

转载自huiytt.iteye.com/blog/1829773