天天看点

MFC框架剖析学习

**MFC框架剖析学习**
           

MFC程序中的WinMain函数

Win32应用程序有一条很明确的主线:首先进入WinMain函数,然后设计窗口类,注册窗口类,产生窗口,显示窗口,更新窗口,最后进入消息循环,将消息路由到窗口过程函数中去处理。

但是在编写MFC程序时并没有这样一条主线,是因为微软在MFC底层框架封装了这些每一个窗口的应用程序都需要的步骤。MFC的WinMain函数是在程序编译链接时,由链接器将该函数链接到程序中。

WinMain函数在appmodul.cpp这个文件中:

extern "C" int WINAPI

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

         _In_ LPTSTR lpCmdLine, int nCmdShow)

#pragma warning(suppress: 4985)

{

         // call shared/exported
WinMain

         return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}
           

MFC唯一标识应用程序的实例

Win32应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。而对MFC程序来说,通过产生一个应用程序类的对象来唯一标识应用程序的实例。每一个MFC程序仅有一个从应用程序类(CWinApp)派生的类。每一个MFC程序实例有且仅有一个该派生类的实例化对象,也就是theApp全局对象。该对象就表示了应用程序本身。当一个子类在构造之前会先调用父类的构造函数。因此theApp对象的构造函数CTestApp在调用之前,会调用其父类CWinApp的构造函数完成程序运行时的一些初始化工作。

CWinApp类的构造函数定义,在源文件appcore.cpp中

CWinApp::CWinApp(LPCTSTR lpszAppName)

{
     if (lpszAppName != NULL)

             m_pszAppName = _tcsdup(lpszAppName);

     else

             m_pszAppName = NULL;



     // initialize CWinThreadstate

     AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();

     ENSURE(pModuleState);

     AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;

     ENSURE(pThreadState);

     ASSERT(AfxGetThread() == NULL);

     pThreadState->m_pCurrentWinThread = this;

     ASSERT(AfxGetThread() == this);

     m_hThread = ::GetCurrentThread();

     m_nThreadID= ::GetCurrentThreadId();//initialize CWinApp state

     ASSERT(afxCurrentWinApp == NULL); // only one CWinApp objectplease

     pModuleState->m_pCurrentWinApp = this;

     ASSERT(AfxGetApp() == this);
     
     pModuleState->m_pCurrentWinApp= this;
           

根据C++继承性原理,这个this对象代表的是子类CTestApp的对象,即theApp。

AfxWinMain函数

当程序调用了CWinApp类的构造函数,并执行了CTestApp类的构造函数,且产生了theApp对象之后,接下来就进入WinMain函数

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
           

可以发现WinMain函数实际上是通过调用AfxWinMain函数来完成它的功能。

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

     _In_ LPTSTR lpCmdLine, int nCmdShow)

{

     ASSERT(hPrevInstance == NULL);

     intnReturnCode = -1;

     CWinThread* pThread = AfxGetThread();

     CWinApp* pApp = AfxGetApp();

     // AFX internalinitialization

     if(!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

             goto InitFailure;
           

其中

CWinThread* pThread = AfxGetThread();CWinApp* pApp = AfxGetApp();

因为MFC中CWinApp派生于CWinThread,所以pThread和pApp这两个指针是一致的。由CWinApp构造函数的代码可以知道AfxGetApp函数返回的是在CWinApp构造函数中保存的this指针。对程序来说,这个this指针实际上指向的是CTestApp的对象theApp。也就是说,对程序来说,pThread和pApp所指向的都是CTestApp类的对象,即theAPP全局对象。

if (pApp != NULL && !pApp->InitApplication())

pApp首先调用InitApplication函数,该函数完成MFC内部管理方面的工作。

if (!pThread->InitInstance())

接着调用pThread的InitInstance函数实现初始化的工作

[

在MFC中CWinApp类派生于CThread,在程序中,从CWinApp类派生了应用程序类CTestApp。在CTestApp类中也有一个InitInstance函数,因为InitInstance函数本身是一个虚函数,根据多态性的原理,可知AfxWinMain函数这里实际上调用的是子类CTestApp的InitInstance函数

虚函数:通过指向派生类的基类指针,访问派生类中同名覆盖成员函数

]

nReturnCode= pThread->Run()

完成消息循环功能。

设计和注册窗口

MFC已经预定义了一些默认的标准窗口类,只需要选择所需要的窗口类然后注册。

窗口类的注册是由AfxEndDeferRegisterClass函数完成的,该函数位于wincore.cpp 中。

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)

{

     // mask off all classes that are alreadyregistered

     AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

     fToRegister &=~pModuleState->m_fRegisteredClasses;

     if (fToRegister == 0)

             return TRUE;



     LONG fRegisteredClasses = 0;



     // common initialization

     WNDCLASS wndcls;

     memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults

     wndcls.lpfnWndProc = DefWindowProc;

     wndcls.hInstance =AfxGetInstanceHandle();

     wndcls.hCursor = afxData.hcurArrow;

     INITCOMMONCONTROLSEX init;

     init.dwSize = sizeof(init);



     // work to register classes as specified byfToRegister, populate fRegisteredClasses as we go

     if (fToRegister &AFX_WND_REG)

     {

             // Child windows - no brush, no icon, safestdefault class styles

             wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

             wndcls.lpszClassName = _afxWnd;

             if (AfxRegisterClass(&wndcls))

                      fRegisteredClasses |= AFX_WND_REG;

     }
           

AfxEndDeferRegisterClass函数首先判断窗口类的类型,然后赋予其相应的类名(wndcls.lpszClassName变量)。之后调用AfxRegisterClass函数注册窗口类,也位于wincore.cpp中。

在创建的MFC程序中,实际上有两个窗口。其中一个是CMainFrame类的对象所代表的应用程序框架窗口。该类有一个PreCreatWindow函数,这是在窗口产生之前被调用的。

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

     if( !CFrameWnd::PreCreateWindow(cs) )

             return FALSE;

     return TRUE;

}
           

CMainFrame类的PreCreatWindow函数中首先调用CFrameWnd类的

PreCreatWindow函数,定义位于winfrm.cpp中:

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)

{

     if (cs.lpszClass == NULL)

     {

             VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

             cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background

     }



     if (cs.style & FWS_ADDTOTITLE)

             cs.style |= FWS_PREFIXTITLE;



     cs.dwExStyle |= WS_EX_CLIENTEDGE;



     return TRUE;

}
           

该函数调用了AfxDeferRegisterClass函数,可以在afximpl.h文件中找到函数的定义:

#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

可以发现AfxDeferRegisterClass实际上是一个宏,真正的指向是

AfxEndDeferRegisterClass函数。而AfxEndDeferRegisterClass函数的功能就是完成窗口类的注册。

创建窗口

按照win32程序编写的步骤,设计窗口类然后注册窗口类之后就是创建窗口了,在MFC程序中,窗口的创建功能是由CWnd类和CreateEx函数实现的,该函数的声明位于afxwin.h中:

virtual BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

             LPCTSTR lpszWindowName, DWORD dwStyle,

             int x, int y, int nWidth, int nHeight,

             HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL);
           

定义位于wincore.cpp中:

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

     LPCTSTR lpszWindowName, DWORD dwStyle,

     int x, int y, int nWidth, int nHeight,

     HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)

{

     ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) || 

             AfxIsValidAtom(lpszClassName));

     ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));

     

     // allow modification of several commoncreate parameters

     CREATESTRUCT cs;

     cs.dwExStyle = dwExStyle;

     cs.lpszClass = lpszClassName;

     cs.lpszName = lpszWindowName;

     cs.style = dwStyle;

     cs.x = x;

     cs.y = y;

     cs.cx = nWidth;

     cs.cy = nHeight;

     cs.hwndParent = hWndParent;

     cs.hMenu = nIDorHMenu;

     cs.hInstance = AfxGetInstanceHandle();

     cs.lpCreateParams = lpParam;



     if (!PreCreateWindow(cs))

     {

             PostNcDestroy();

             return FALSE;

     }
           

在MFC的底层代码中,CFrameWnd类的Create函数内部调用了父类CWnd函数的CreateEX函数,而Create函数又由CFrameWnd类的LoadFrame函数调用。

CreateEX函数不是虚函数(现在在VS2015中定义已经变为了虚函数),另外CFrameWnd类中并没有重写CreateEX函数,因此CFramewnd类中的Create函数内调用的CreateEX函数就是CWnd类的CreateEX函数。

在CWnd类中的CreateEX函数中调用了PreCreateWindow函数,这是一个虚函数。因此,这里实际上调用的是子类,即CMainFrame类的PreCreatWindow函数。

之所以要再次调用这个函数,就是为了在产生窗口之前让程序员有机会修改窗口外观,例如去掉窗口的最大化按钮等。PreCReatWindow函数的参数就是为了这个功能而提供的。PreCreatWindow函数的参数是CREATETRUCT结构,把CREATETRUCT和CreateWinodwEX函数的声明做一下比较:

typedef struct tagCREATESTRUCT

{

    LPVOID lpCreateParams;

    HINSTANCE hInstance;  

    HMENU hMenu;           

   HWND hwndParent;       

   int cy;               

   int cx;                

    int y;                 

    int x;                 

   LONG style;            

   LPCTSTR lpszName;      

   LPCTSTR lpszClass;     

   DWORD dwExStyle;       

}CREATESTRUCT;

 



HWND CreateWindowEx(
    
DWORD DdwExStyle,      

LPCTSTR lpClassName,  

LPCTSTR lpWindowName, 

DWORD dwStyle,         

int x,                 

int y,                

int nWidth,          

int nHeight,          

HWND hWndParent,     

HMENU hMenu,        

HINSTANCE hInstance,    

LPVOID lpParam);
           

可以看出参数的类型是一致的。同时PreCreatWindow函数的参数是引用类型,这样子类做的修改,基类也会体现出来。所以如果子类修改了PreCreatWindow的值,那么接下来调用CreateEx函数时,其参数也会发生相应的变化,从而会创建一个符合要求的窗口。

显示窗口及更新窗口

在MFC.cpp中有一个名为m_pMainWnd的成员变量。该变量是CWnd类型的指针,它保存了应用程序框架窗口对象的指针。也就是说是指向CMainFrame对象的指针。

CMFCApp类的InitInstance函数实现里的两行代码实现了显示窗口和更新窗口

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();
           

消息循环

消息循环是由CWinThread类的Run函数完成的。该函数是由AfxWinMain函数中调用的,AfxWinMain函数的定义在winmain.cpp中。

CWinThread类的Run函数主要结果是一个for循环,该函数在接收到WM_QUIT消息时退出,在for循环中调用了PumpMessage函数,在该函数中进行了

TranslateMessage和DispatchMessage操作。

窗口过程函数

在wincore.cpp中的AfxEndDeferRegisterClass函数中有:

wndcls.lpfnWndProc =DefWindowProc;
           

这行代码的作用就是设置窗口过程函数,这里指定的是一个默认的窗口过程:

DefWindowProc。但实际上MFC程序并不是把所有的消息都交给DefWindowProc这一默认的窗口过程来处理,而是采用了一种称之为消息映射的机制来处理各种消息的。