**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这一默认的窗口过程来处理,而是采用了一种称之为消息映射的机制来处理各种消息的。