mfc程序执行流程,mfc应用程序的执行过程
1.创建应用程序对象。
在程序开始时,产生一个(且只有一个)应用程序对象theApp,即CWinApp对象。一旦产生了这个全局对象,就会执行它的构造函数。因为没有定义CMyWinApp构造函数,所以执行CWinApp类的构造函数。APPCORE的第75行定义了这个函数。CPP,而且你可以搜出来自己嚼。所以CWinApp中的成员变量会因为全局对象theApp的诞生而获得配置和初始值。
2、WinMain出道
用SDK编程时,程序的入口点是WinMain函数,但是在MFC程序中我们没有看到WinMain函数。哦!~原来她藏在MFC代码里。当App配置完成,WinMain登场,慢一点!仔细看程序,却没有连接WinMain函数的代码!这个我也不知道。MFC已经准备好了,并由链接器直接添加到应用程序代码中。原来她在appmodule.cpp里,好了,我们认为app配置完成后,程序会到appmodule.cpp,那怎么办?看一下从APPMODUL中提取的以下代码。CPP:
外部 C int WINAPI
_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
//调用共享/导出的WinMain
返回AfxWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
}
_tWinMain函数的“_t”是一个为支持Unicode而准备的宏。
_tWinMain函数的返回值是AfxWinMain函数的返回值,在WinMain的第21行定义。CPP稍微整理一下,就能看出这个“程序入口点”主要做什么:
int afx API afx winmain(h instance h instance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
int nReturnCode=-1;
cwin app * pApp=AfxGetApp();
AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
pApp-init application();
pApp- InitInstance()
nReturnCode=pApp-Run();
afx winterm();
返回nReturnCode
}
AfxGetApp()函数是获取CMyWinApp对象指针的,所以上面第6行到第8行函数相当于调用:
CMyWinApp:init application();
CMyWinApp:InitInstance()
CMyWinApp:Run();
从而引起呼叫:
cwin app:init application();//因为CMyWinApp不重写InitApplication
CMyWinApp:InitInstance() //因为CMyWinApp重写了InitInstance。
cwin app:Run();//因为CMyWinApp不会覆盖Run
用过SDK写程序的朋友,现在可能会心一笑。
3.AfxWinInit——AFX内部初始化操作
AfxWinInit是CWinApp构造函数之后的第一个操作,主要做AFX的内部初始化,处理很多意外问题。它对应于AfxWinTerm()。该函数在APPINIT的第24行中定义。CPP,所以这里就不拿出来了。搜出来自己嚼!
4.执行CWinApp:InitApplication
AfxWinInit之后,操作是pApp- InitApplication。我们已经知道pApp指向CMyWinApp对象。拨打电话时:
pApp-init application();
相当于调用:
CMyWinApp:init application();
但是你要知道CMyWinApp继承了CWINAP,InitApplication是CWINAP的虚函数。我们没有重写(大多数情况下,我们不需要重写),所以上面的操作相当于调用:
cwin app:init application();
APPCORE的第125行定义了这个函数。CPP自己搜出来!我不会搬出去。里面所有的操作都是MFC为了内部管理而做的(其实我也看不懂。知道有这种事就好了)。
5.执行CWinApp:InitInstance
AfxWinMain在InitApplication函数后调用pApp- InitInstance。当程序调用:
pApp-InitInstance();
相当于调用:
CMyWinApp:init instance();
但是你要知道CMyWinApp继承了CWINAP,InitInstance是CWINAP的虚函数。我们已经重写过了,上面的操作就是调用我们自己的这个InitInstance函数(CMyWinApp)。
6.CFrameWnd:Create生成主窗口(并首先注册窗口类)
现在我们来到CWinApp:InitInstance,它首先创建一个CMyFrameWnd对象来生成主窗口。在创建CMyFrameWnd对之前,应该首先执行构造函数CMyFrameWnd:CMyFrameWnd(),该函数使用Create函数生成一个窗口:
CMyFrameWnd:CMyFrameWnd()
{
Create(NULL, Hello MFC ,WS_OVERLAPPEDWINDOW,rectDefault,NULL, main menu );
}
Create是CFrameWnd的成员函数,会产生一个窗口。用过SDK编程的朋友都知道,创建主窗口时,必须注册一个窗口类,指定窗口的属性等。但是,这里用的是哪个窗口类呢?Create函数的第一个参数(其他参数请参考MSDN或《深出浅出MFC》)指定将窗口类设置为NULL。这是什么意思?意思是用MFC内置的air类生成一个标准的大纲窗口,但是我们的程序一般不注册任何窗口类!哦,Create函数会在窗口生成之前触发窗口类的注册。
我们先来挖掘一下Create函数做了什么。在WINFRM的第538行定义了Create函数。CPP(这里不复制代码,大家自己打开看看)。该函数在第562行调用CreateEx函数。由于CreateEx是CWnd的成员函数,而CFrameWnd是从CWnd派生出来的,所以会调用CWnd:CreateEx。WINCORE的665行定义了这个函数。CPP是代码的一部分:
BOOL CWnd:CreateEx(DWORD dwex style,LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,DWORD dwStyle,
int x,int y,int nWidth,int nHeight,
HWND hWndParent,HMENU nIDorHMenu,LPVOID lpParam)
{
//允许修改几个常见的创建参数
创建结构化cs;
cs . dwex style=dwex style;
cs . lpsz class=lpsz class name;
cs.lpszName=lpszWindowName
cs.style=dwStyle
cs.x=x
cs.y=y
cs.cx=nWidth
cs.cy=nHeight
cs . hwnd parent=hwnd parent;
cs . hm enu=nIDorHMenu;
cs . h instance=AfxGetInstanceHandle();
cs.lpCreateParams=lpParam
if(PreCreateWindow(cs))
{
post ncdestroy();
返回FALSE
}
AfxHookWindowCreate(this);
HWND HWND=:CreateWindowEx(cs . dwex style,cs.lpszClass,
cs.lpszName,cs.style,cs.x,cs.y,cs.cx,cs.cy,
cs.hwndParent,cs.hMenu,cs.hInstance,cs . lpcreateparams);
.
}
用过SDK编程的朋友看到上面的代码应该有点感觉了。函数中调用的PreCreateWindows是一个虚函数,在CWnd和CFrameWnd中定义。因为这个指针指向的对象,这里应该调用CFrameWnd:PreCreateWindow。WINFRM的第521行定义了该函数。CPP是代码的一部分:
BOOL CFrameWnd:pre create window(create struct cs)
{
if (cs.lpszClass==NULL)
{
VERIFY(AfxDeferRegisterClass(AFX _ WNDFRAMEORVIEW _ REG));
cs . lpsz class=_ afxWndFrameOrView;//颜色_窗口背景
}
.
}
AfxDeferRegisterClass是AFXIMPL中定义的宏。H.宏如下所示:
#定义AfxDeferRegisterClass(fClass)afxendferregisterclass(fClass)
注意:这里的宏与《深入浅出MFC》不同。以上代码摘自Visual C 6.0。
AfxEndDeferRegisterClass是在WINCORE的3619行中定义的。CPP这个函数很复杂,主要注册五个窗口类(哇!终于看到了窗口类。我如何能使用五?还不知道),就在窗口生成之前调用不同类的PreCreateWindow成员函数,准备注册窗口类。如果指定的窗口类为空,将使用系统默认的类。从CWnd及其派生类的PreCreateWindow成员函数中,我们可以看到对于不同功能的窗口,整个框架使用了哪些窗口类。
7.窗口显示和更新
CMyFrameWnd:CMyFrameWnd结束后,窗口已经诞生;进程返回CMyWinApp:InitInstance,于是调用ShowWindow函数显示窗口,调用UpdateWindow函数发送WM_PAINT消息。在SDK程序中,通过窗口函数处理消息。现在,窗口功能在哪里,如何交付给it?先说CWinApp:Run。
8.执行CWINAP:运行——程序,活水之源。
执行CMyWinApp:InitInstance函数后,程序进入AfxWinMain函数的pApp- Run。现在我们知道这会执行CWinApp:Run函数,这个函数是在APPCORE的第391行定义的。CPP这里是程序代码:
int CWinApp:Run()
{
if(m _ pMainWnd==NULL AfxOleGetUserCtrl())
{
//没有启动/嵌入或/自动化,但是没有主窗口!
TRACE0(警告:CWinApp:Run -退出应用程序中的m_pMainWnd为空。/n’);
AfxPostQuitMessage(0);
}
返回CWinThread:Run();
}
该函数调用CWinThread:Run函数,该函数在THRDCORE的第456行中定义。CPP我这里就不抄了。PumpMessage函数在函数的第480行调用,该函数在THRDCORE的第810行定义。CPP排序后的部分代码如下:
BOOL CWinThread:PumpMessage()
{
如果(!* GetMessage(m _ msg cur,NULL,NULL,NULL))
{
返回FALSE
}
//处理此消息
if (m_msgCur.message!=WM_KICKIDLE!PreTranslateMessage( m_msgCur))
{
translate message(m _ msg cur);
* dispatch message(m _ msg cur);
}
返回TRUE
}
该函数的主要操作是将消息从:DispatchMessage发送到窗口函数(CWnd:DefWindowProc),但程序一般不提供任何窗口函数。但是,在AfxEndDeferRegisterClass中,窗口函数被指定为:
wnd cls . lpfnwndproc=DefWindowProc;
虽然窗口函数被指定为DefWindowProc(CWnd:DefWindowProc)的成员函数,但实际上消息并没有被泵到那里,而是一个名为AfxWndProc的全局函数。
9.——用于连接消息和处理功能的消息映射机制
此时,主窗口已经生成,等待各种消息,然后调用相应的处理函数。但是,消息和处理功能是如何连接在一起的呢?MFC采用消息映射机制(message mapping mechanism),为应用程序提供了两组具有“非常方便的接口”的宏。原理我也不太清楚,这里也没法解释。主要用法是:第一,在类声明中结合DECLARE_MESSAGE_MAP()给出处理函数,比如:
类CMyFrameWnd:公共CFrameWnd
{
公共:
CMyFrameWnd();
afx _ msg void OnPaint();//对于WM_PAINT
afx _ msg void on about();//for WM_COMMAND (IDM_ABOUT)
声明消息映射()
}
然后在对应的任意位置使用BEBIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏。CPP文件(当然,不在函数内)添加相应的消息,例如:
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_COMMAND(IDM_ABOUT,OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
为什么这样的宏之后消息会自动流向指定的函数?答案在于消息图的结构设计。自己找《深入浅出MFC》第三章的消息图模拟程序!