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

长平狐 发布于 2012/10/08 15:16
阅读 153
收藏 1

第六章 MFC程序的生死因果

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

MFC程序需要windows C runtime函数库、DLL import函数库以及MFC函数库。

Windows C runtime函数库为:

LIBC.LIB  静态链接版本

MSVCRT.LIB  动态链接版本

MSVCRTD.LIB D  ebug版本。

DLL import函数库

   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    凡使用通用对话框(Common dialog)的MFC程序需要载入此文件。它内部包含COMMDLG.H

   5:FXCMN.H       凡使用windows 9x新增通用行控件(common control)的MFC程序需要载入此文件。

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

   7AFXDLLX.H    凡使用MFC extension DLLs需要载入此文件。

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

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

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

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

   1virtual bool InitApplication();

   2virtual bool InitInstance();

   3virtual int Run();

此外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_msg void OnPaint(); 

   afx_msg void OnAbout();

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

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

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

extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,           HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)

{

   return AfxWinMain(hInstance,hPrevInstance ,lpCmdLine,

                                         nCmdShow);

}

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

Int AFXAPI AfxWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdLine)

{

   Int nReturnCode=-1;

   CWinApp *pApp=AfxGetApp();

   AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow);

   pApp->InitApplication()

   pApp->InitInstance();

   nReturnCode=pApp->Run();

}

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

_AFXWIN_INLINE  CWinApp*AFXAPI AfxGetApp()

{

   return afxCurrentWinApp;

}

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

#define afxCurrentWinApp\

 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定义:

bool CFrameWnd::PreCreareWindow(CREATESTRUCT&cs)

{

   if(cs.lpszClass==NULL)

    {

      AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);

     cs.lpszClass=_afxWndFrameOrView;

    }

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

}

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

#define AfxDeferRegisterCalss(fClass)\

     ((afxRegisteredClasses&fClass)?true\

        :AfxEndDeferRegisterClass(fClass);

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

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

#define afxRegisterClasses AfxGetModuleState->

                          m_fRegisteredClasses();

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

bool CWnd::PreCreateWindowCREATESTRUCT&cs)

{

   if(cs.lpszClass==NULL)

   {

         AfxDeferRegisterClass(AFX_WND_REG);

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

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

   }

}

bool CFrameWnd::PreCreateWindowCREATESTRUCT&cs)

{

   if(cs.lpszClass==NULL)

   {

         AfxDeferRegisterClass(AFX_WND_REG);

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

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

   }

}

bool CMDIFrameWnd::PreCreateWindowCREATESTRUCT&cs)

{

   if(cs.lpszClass==NULL)

   {

         AfxDeferRegisterClass(AFX_WND_REG);

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

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

   }

}

bool CMDIChildWnd::PreCreateWindowCREATESTRUCT&cs)

{

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

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

}

bool CView::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中。

int CWinApp::Run()j

{

   if(m_pMainWnd==NULL&&AfxOleGetUserCtrl)

    {

      AfxPostQUitMessage(0);

    }

   return CWinThread::Run();

}

Int CWinThread::Run()

{

   Bool bIdle=true;

   Long iIdleCoun=0;

   While(1)

  {

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

              NULL,NULL,PM_NOREMOVE))

       {

          If(!OnIdle(lIdleCount++))

             bIdle=true;

       }

   }

  Do

   {

      If(!PumpMessage())

          Return ExitInstance();

      If(IsIdleMessage(&m_msgCur))  

      {

         bIdle=0;

         lIdleCount=0;

      }

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

                                 NULL,PM_NOREMOVE);

}

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

Bool CWinThread::PumpMessage()

{

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

   {

      Return false;

   }

   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_msg void类型的 。

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

程序的诞生过程:

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

2AfxWinMainzhxing AfxWinInit,后者又调用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类型。线程入口函数也一样


原文链接:http://blog.csdn.net/ithzhang/article/details/7644481
加载中
返回顶部
顶部