深入浅出MFC学习笔记(第6章 :MFC程序的生死因果)

第六章MFC程序的生死因果

本章主要是从MFC程序代码中,找出一个windows程序原本该有的程序入口点、窗口类注册、窗口产生、消息循环、窗口函数等操作。抽丝剥茧彻底理解一个MFC程序的诞生与结束。

MFC程序需要windowsCruntime函数库、DLLimport函数库以及MFC函数库。

WindowsCruntime函数库为:

LIBC.LIB静态链接版本

MSVCRT.LIB动态链接版本

MSVCRTD.LIBDebug版本。

DLLimport函数库

GDI32.LIB

USER32.LIB

KERNEL32.LIB

MFC函数库

MFC42.LIB

MFC42D.LIB

MFCS42.LIB

MFCS42D.LIB

......

SDK程序需要载入windows.hMFC程序需要另外一些.h文件:

1STDAFX.H它用来载入其他MFC头文件。

2AFXWIN.H它和它载入的文件声明了所有的MFC类。其内包含AFX.H,后者包含了AFXVER_.H,后者又载入了AFXV_W32.H,后者又载入WINDOWS.H

3AFXEXT.H使用工具栏、状态栏的程序必须载入这个文件

4AFXDLGS.H凡使用通用对话框(Commondialog)的MFC程序需要载入此文件。它内部包含COMMDLG.H

5:FXCMN.H凡使用windows9x新增通用行控件(commoncontrol)的MFC程序需要载入此文件。

6AFXCOLL.H凡使用MFC提供的容器都需要载入此文件。

7AFXDLLX.H凡使用MFCextensionDLLs需要载入此文件。

8AFXRES.HMFC程序的RC文件必须载入此文件。此文件中对于标准资源的ID都有默认值。它们定义于此文件中。

MFC头文件均置于MFC\INCLUDE中。

MFC把有着相当固定行为的WinMain内部操作,封装在CWinApp中。把有着相当固定行为的WndProc内部操作封装在CFrameWnd中。也就是说CWinApp代表程序本体。CFrameWnd代表主框窗口。程序员必须以这两个类为基础,派生自己的类,并改写一部分成员函数,以产生适合自己的程序。

CWinApp取代WinMain,并不是说MFC没有WinMain而是CWinApp完成了由传统WinMain完成的工作。主要工作由CWinApp的三个成员函数完成:

1virtualboolInitApplication();

2virtualboolInitInstance();

3virtualintRun();

此外CWinApp中还有一个从CWinThread继承而来的m_pMainWnd,用以记录程序的主窗口。

以下为CWinApp中有的成员变量:

m_hInstance当前实例句柄

m_hPrevInstance前一实例句柄。

m_lpCmdLine命令行参数

m_CmdShow显示类型

它们是SDK程序中传给WinMain的四个参数。

m_pMainWnd主窗口

m_pActiveWnd当前激活窗口。

m_pszAppName程序名

以上几个可以在InitInstance中修改。

m_pszExeName可执行程序名称。

m_pszHelpFilePath

m_pszProfileName

CFrameWnd主要用来掌握一个窗口,它取代了WndProc的地位。

我们派生的CMyFrameWnd类应该定义消息响应函数。如

afx_msgvoidOnPaint();

afx_msgvoidOnAbout();

使用DELCARE_MESSAGE_MAP将消息和消息处理函数关联。这在前面章节已经介绍过。

当程序执行时,全局的theApp对象产生,于是调用构造函数,并初始化各成员变量。一系列的操作使整个类继承体系被调动起来。

theAppWinMain之前被构造,此后WinMain被调用。WinMain早已被写好,被链接器接入到程序中。_tWinMain中的_t是为了支持Unicode而支持的宏。它实际上就是WinMain,就像console程序里_tmainmain的关系。

extern"C"intWINAPI_tWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdShow)

{

returnAfxWinMain(hInstance,hPrevInstance,lpCmdLine,

nCmdShow);

}

AfxWinMain函数位于MFCWINMAIN.CPP文件中。稍加整理发现它主要做了以下事:

IntAFXAPIAfxWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdLine)

{

IntnReturnCode=-1;

CWinApp*pApp=AfxGetApp();

AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow);

pApp->InitApplication()

pApp->InitInstance();

nReturnCode=pApp->Run();

}

AfxGetApp是一个全局函数,它定义于AFXWIN1.INL

_AFXWIN_INLINECWinApp*AFXAPIAfxGetApp()

{

returnafxCurrentWinApp;

}

afxCurrentApp是一个宏,定义域AFXWIN.H中。

#defineafxCurrentWinApp\

AfxGetModuleState()->m_pCurrentWinApp

AfxWinInit是继CWinApp构造函数之后的第一个操作,它进行一些对theApp对象的初始化操作,如将传给WinMain的四个参数赋值给theApp的对应成员、初始化线程等等。

CMyWinApp继承自CWinApp,没有改写虚函数InitApplication,因此调用的是CWinAppInitApplication。在前面的关键技术仿真时,我们在Initapplication里注册了窗口类,但是MFC并不是这样做的。CMyWinApp重写了InitInstance函数,因此pApp->InitInstance调用的是CMyWinApp的版本。在此函数内,MFC构造了CMyFrameWnd,在CMyFrameWnd的构造函数内调用了Create函数,它产生一个窗口,它需要八个参数,其中六个都有了默认值。只有前两个需要指定。

第一个参数lpszClassName,用以指定WNDCLASS类,使用NULL表示使用MFC内建的窗口类产生一个标准的窗口,但是此时我们并没与发现窗口注册的操作。因为在Create函数内会调用注册窗口类的函数。这稍候会做介绍。

第二个参数:lpszWindowName,指定窗口标题。很简单不介绍。

第三个参数指定窗口风格。

第四个参数指定窗口的位置与大小。默认值rectDefaultCFrameWnd的一个static成员变量。告诉windows以默认方式指定窗口位置和大小。同时也可以手动指定,如CRect(40,20,240,460);

第五个参数用以指定父窗口。对于一个顶层窗口来说它应该为NULL,表示它没有父窗口。但是其实它是有的,它的父窗口是desktop窗口。

第六个参数指定菜单,它是在RC文件中定义的。

第八个参数pContext指向CCreateContext结构指针,MFC利用它在文档视图结构初始化外框窗口。不具备文档视图结构的程序此值为NULL.

CFrameWnd::Create函数在调用时,会引发窗口类的注册操作,这是通过CreateEx来实现的。由于CFrameWnd没有重写CreateEx。所有调用的CreateEx实际调用的是其父类CWndCreateEx函数。

CreateEx会进行窗口的注册,但这是通过PreCreateWindow进行的,CWndCFrameWnd都定义了PreCreateWindow函数,但由于this指针指向的类型的缘故,此处调用的是CFrameWndPreCreateWindow

PreCreateWindow定义:

boolCFrameWnd::PreCreareWindow(CREATESTRUCT&cs)

{

if(cs.lpszClass==NULL)

{

AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);

cs.lpszClass=_afxWndFrameOrView;

}

//................

}

AfxDeferRegisterClass是一个定义于AFXIMPL.H的宏。

#defineAfxDeferRegisterCalss(fClass)\

((afxRegisteredClasses&fClass)?true\

:AfxEndDeferRegisterClass(fClass);

这个宏表示如果afxRegisterClasses的值显示系统已经注册了fClass,窗口类就啥也不做。否则就调用AfxEndDeferRegisterClass注册该窗口类。

afxRegisterClasses是一个旗标变量,用来记录已经注册了哪些窗口类。

#defineafxRegisterClassesAfxGetModuleState->

m_fRegisteredClasses();

不同类的PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备来注册窗口类。如果指定的窗口类为NULL,那么就是用系统默认类。MFC定义了五个不同的默认类。从CWnd及其各个派生类的成员函数PreCreateWindow可以看出,针对不同的窗口使用哪些窗口类。

boolCWnd::PreCreateWindowCREATESTRUCT&cs)

{

if(cs.lpszClass==NULL)

{

AfxDeferRegisterClass(AFX_WND_REG);

//...................

cs.lpszClass=_afxWnd;//CWnd类使用默认的_afxWnd类。

}

}

boolCFrameWnd::PreCreateWindowCREATESTRUCT&cs)

{

if(cs.lpszClass==NULL)

{

AfxDeferRegisterClass(AFX_WND_REG);

//...................

cs.lpszClass=_afxWndFrameOrView;//CFrameWnd类使用默认的_afxWndFrameOrView类。

}

}

boolCMDIFrameWnd::PreCreateWindowCREATESTRUCT&cs)

{

if(cs.lpszClass==NULL)

{

AfxDeferRegisterClass(AFX_WND_REG);

//...................

cs.lpszClass=_afxWndMDIFrame;//CMDIFrameWnd类使用默认的_afxWndFrameOrView类。

}

}

boolCMDIChildWnd::PreCreateWindowCREATESTRUCT&cs)

{

///,...............................

ReturnCFrameWnd::PreCreateWnd(cs);//表示此类使用的窗口类是_afxWndFrameOrView;

}

boolCView::PreCreateWindowCREATESTRUCT&cs)

{

if(cs.lpszClass==NULL)

{

AfxDeferRegisterClass(AFX_WND_REG);

//...................

cs.lpszClass=_afxWndFrameOrView;//CView类使用默认的_afxWndFrameOrView类。

}

}

这五种默认的类对应五种窗口。

CMyFrameWnd::CMyFrameWnd结束后,窗口已经产生。调用ShowWindow令窗口显示出来,调用UpdateWindow函数发送WM_PAINT消息使窗口重绘。

pApp->Run();相当于CMyWinApp::Run,由于CMyWinApp继承自CWinApp但并未改写Run,因此相当于调用CWinApp::Run();它被定义在APPCORE.CPP中。

intCWinApp::Run()j

{

if(m_pMainWnd==NULL&&AfxOleGetUserCtrl)

{

AfxPostQUitMessage(0);

}

returnCWinThread::Run();

}

IntCWinThread::Run()

{

BoolbIdle=true;

LongiIdleCoun=0;

While(1)

{

While(idle&&!::PeekMessage(&m_msgCur,NULL,

NULL,NULL,PM_NOREMOVE))

{

If(!OnIdle(lIdleCount++))

bIdle=true;

}

}

Do

{

If(!PumpMessage())

ReturnExitInstance();

If(IsIdleMessage(&m_msgCur))

{

bIdle=0;

lIdleCount=0;

}

}while(::PeekMessage(&m_msgCur,NULL,NULL,

NULL,PM_NOREMOVE);

}

CWinThread::Run中,消息循环调用了PeekMessage,看消息队列是否有消息,如没有同时当前为空闲状态亲爱,就调用OnIdle函数。

BoolCWinThread::PumpMessage()

{

If(!::GetMessage(&m_msgCur,NULL,NULL,NULL)

{

Returnfalse;

}

If(m_msg.message!=WM_KICKIDLE&&

!PreTranslateMessage(&m_msgCur)

{

::TranslateMessage(&m_msgCur);

::DispatchMessage(&m_msgCur);

}
}

窗口函数实际上也是由MFC提供的,如DefWndProcWinMainMFC提供,窗口类也由MFC注册完成,连窗口函数都由MFC提供,这大大减轻了程序员的工作量。

MFC把消息分为三大类:

1:标准Windows消息。WM_xx。消息与消息响应函数的对应规则为,如

ON_WM_CHARWM_CHAROnChar);

ON_WM_CLOSE(WM_CLOSE,OnClose);

ON_WM_DESTROY(WM_DESTROY,OnDestroy);

2:命令消息,WM_COMMAND。消息与消息响应函数的对应规则为:

ON_COMMAND(IDM_ABOUT,OnAbout);

ON_COMMAND(ID_FILEOPEN,OnFileOpen);

3:通知消息。有控件产生.

Button控件ON_BN_CLICKED(id,memberFunc);

Edit控件ON_EN_SETFOCUS(id,memberFunc);

各个消息函数都是以afx_msgvoid类型的。

如果某个消息在消息映射表中找不到对应项,则它会向基类流动,这被称为消息传递(MessageRouting)。如果流动到最基础的类,仍然无法找到对应项,则会有默认的函数来处理。就像SDK程序的DefWindowProc一样。从CCmdTarget派生的类都可以设定自己的MessageMap

程序的诞生过程:

1CMyWinApp对象产生,初始化各成员变量。

2AfxWinMainzhxingAfxWinInit,后者又调用AfxInitThread,把消息队列加大到96.

3AfxWinMain调用InitInstance,我们必须改写它。

4CMyWinApp::InitInstancenew了一个CMyFrameWnd对象。

5:在CMyFrameWnd构造函数内调用Create产生主窗口,为Create的窗口类传入NULL,于是根据MFC窗口种类自动注册窗口。

6:调用ShowWindow显示窗口。

7:调用UpdateWindow发出WM_PAINT

8:执行Run,进入消息循环。

9:程序获得WM_PAINT消息,。

10WM_PAINT经由::DisPatchMessage送到CWnd::DefWindowProc中。

11:在消息映射表查找此消息,并调用响应消息处理函数。

标准消息的处理程序有标准的命名,如WM_PAINTOnPaint处理。

程序结束过程:

1:单击Close,发出WM_CLOSE

2CMyFrameWnd没有设置WM_CLOSE处理函数,因此交由默认处理函数处理。

3:默认处理函数调用::DestroyWindow发出WM_QUIT.

4CWinApp::Run收到WM_QUTI后会结束其内部的消息循环,然后调用ExitInstance

5:回到WinMain函数,执行AfxWinTerm结束程序。

Callback是回调函数,它是被windows系统调用的。因为某些windowsAPI函数要求以callback函数作为其参数之一,如SetTimer,此函数会在条件满足时调用该callback函数。当类的成员作为windowscallback函数时,该成员函数必须是static类型。线程入口函数也一样

你可能感兴趣的:(学习笔记)