MAF APPWizard是一个辅助生成代码的向导工具,可以帮助自动生成基于MFC框架的源代码。创建一个MFC的单文档界面(SDI)应用程序,命名为Windows编程。
查看一下类视图(ClassView)的标签页,可以看到五个非常重要的类。
类名以C开头,打开折叠项,发现他们都是由CObject派生。新奇的查阅一下MSDN中的层次结构图。CWnd类是非常重要的一个类。https://msdn.microsoft.com/zh-cn/library/ws8s10w4.aspx
I.WinMain函数
0.WinMain函数
Win32应用程序中,有1)WinMain函数;2)设计窗口类,注册窗口类,产生窗口,显示窗口,更新窗口;3)消息循环清晰地程序脉络。在MFC中虽然在工程中找不到这样的脉络,但是MS在MFC底层框架类中封装了这些步骤。
Edit->Find inFiles 在MFC安装目录下可以找到这些脉络。
在appmodul.cpp中找到了WinMain类似的函数,_tWinMian,调用AfxWinMain。在_tWinMian处F9下断点,程序停留在_tWinMian处。说明该_tWinMain(HINSTANCEhInstance, HINSTANCEhPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow)就是Win32中的WinMain。
1.theApp全局对象
找到了WinMain,那么它是如何将MFC程序的各个类组织到一起的?也就是说,MFC程序中的类是如何与WinMain函数关联起来的?
可以双击CWindows编程App类,跳转到定义的头文件中。CWindows编程App派生于CWinAppEx类(应用程序类),在CWindows编程App构造函数处设置断点,发现比先进入构造函数,在进入WinMain。
// 唯一的一个 CWindows编程App 对象
CWindows编程App theApp;
这里,回顾一下C++四种不同的对象的生与死问题:
①对于一般局部对象(栈Stack中产生):当对象产生时,构造函数被执行;当函数结束时(以至于对象将毁灭),析构函数被执行。
②对于new操作局部对象(堆Heap中产生):当对象产生时(执行new操作),构造函数被执行;当delete对象语句被执行时,析构函数被执行。
③对于全局对象:程序一开始,其构造函数就先被执行(比入口函数main、WinMain更早);程序结束前,析构函数被执行。
④对于局部静态(static)对象:只会有一个实例产生,而且在固定的内存上(既不是stack也不是heap),执行到第一次声明处(也就是在MyFunc第一次调用)时,构造函数被调用;当程序结束时(对象因此遭致毁灭),析构函数被执行,但比全局对象的析构函数先一步执行。
TheAPP对象的构造函数在调用之前,会调用其父类CWinApp的构造函数,从而将自己创建的类与Microsoft提供的基类关联起来。CWinApp的构造函数完成了程序运行时的一些初始化工作。
//initialize CWinApp state
ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
this是theApp。同时CWinApp有一个形参lpszAppName。(CWinApp::CWinApp(LPCTSTRlpszAppName))但CWindows编程App构造函数没有形参,那子类将显式的调用基类带参数的构造函数。然而没有这么做的原因是CWinApp构造函数中设置了NULL。
class CWinApp : public CWinThread
{
DECLARE_DYNAMIC(CWinApp)
public:
// Constructor
explicit CWinApp(LPCTSTR lpszAppName = NULL); // app name defaults to EXE name
2.AfxWinMain函数
程序调用CWinApp类的构造函数,以及CWindows编程App的构造函数,生成theApp对象后进入WinMain函数。WinMain实际调用AfxWinMain。在文件中找到AfxWinMain定义代码
/
//Standard WinMain implementation
// Can be replaced as long as 'AfxWinInit' iscalled first
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
//AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
gotoInitFailure;
//App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
gotoInitFailure;
//Perform specific initializations
if (!pThread->InitInstance())
{
if(pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning:Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode =pThread->ExitInstance();
gotoInitFailure;
}
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
//Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning:Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
调用AfxGetThread得到一个CWinTread类型指针。AfxGetApp得到一个CWinApp指针。AfxGetThread其实调用了AfxGetApp,所以pThread和pApp一致。
_AFXWIN_INLINECWinApp* AFXAPI AfxGetApp()
{ return afxCurrentWinApp; }
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
由CWinApp构造函数返回的是CWinApp中保存的this,所以知道pThread、pApp返回的是theApp全局对象。
3.InitInstance函数
virtual BOOL InitInstance();
虚函数调用的是子类CWindows编程的InitInstance
BOOL CWindows编程App::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
// 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
// 使用 RichEdit 控件需要AfxInitRichEdit2()
//AfxInitRichEdit2();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
//TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
// 注册应用程序的文档模板。 文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CWindows编程Doc),
RUNTIME_CLASS(CMainFrame), // 主SDI 框架窗口
RUNTIME_CLASS(CWindows编程View));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令。 如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
II.MFC框架窗口
1. 设计和注册窗口
MFC已经预设计了一些标准窗口类,只需要AfxEndDeferRegisterClass注册就好。AfxEndDeferRegisterClass在WINCORE.cpp中。AfxEndDeferRegisterClass代码可以知道首先判断窗口类型,赋予类名(wndcls.lpszClassName),调用AfxRegisterClass。AfxRegisterClass首先获得窗口类信息,未注册则调用RegisterClass注册。
在CMainFrame类PreCreateWindow函数设断点,发现程序在调用theApp和WinMain后到达,所以MFC在WinMain后注册窗口类的。
2.创建窗口
在设计和注册窗口类后是创建窗口,由CWnd类的CreateEx函数完成。声明在AfxWin.h中
virtual BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTRlpszWindowName, DWORD dwStyle,
const RECT&rect,
CWnd*pParentWnd, UINT nID,
LPVOIDlpParam = NULL);
代码在wincore.cpp中,调用了CreateWindowEx函数;在MFC底层代码中CFrameWnd类调用了上述CreateEx,而前者又有LoadFrame调用。CFrameWnd派生于CWnd类,CWnd类的不是虚函数,所以CFrameWnd继承而没有重写。
3.显示窗口和更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
III.消息循环
AfxWinMain中的pThread->Run();CWinThread类的Run函数的定义。
// mainrunning routine until thread exits
int CWinThread::Run()
{
ASSERT_VALID(this);
_AFX_THREAD_STATE* pState = AfxGetThreadState();
//for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
//acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while(bIdle &&
!::PeekMessage(&(pState->m_msgCur),NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if(!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if(!PumpMessage())
returnExitInstance();
// reset "no idle" state after pumping"normal" message
//if (IsIdleMessage(&m_msgCur))
if(IsIdleMessage(&(pState->m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&(pState->m_msgCur),NULL, NULL, NULL, PM_NOREMOVE));
}
}
主要结构是一个for循环,在收到WM_QUIT消息退出,在循环中调用了一个PumpMessage。
IV.窗口过程函数
wndcls.lpfnWndProc = DefWindowProc;
该行代码作用是设置窗口过程函数,默认的窗口过程:DefWindowProc。并不是将所有消息交给DefWindowProc处理,而是采取消息映射的机制。
至此,了解了MFC程序的整个运行机制。