现在的位置: 首页 > 综合 > 正文

微软基础类库MFC概述

2017年10月04日 ⁄ 综合 ⁄ 共 12235字 ⁄ 字号 评论关闭

           微软基础类库(MFC:Microsoft Foundation Class)是微软为Windows程序员提供的一个面向对象的Windows编程接口,它大大简化了Windows编程工作。使用MFC类库的好处是:

         首先,MFC提供了一个标准化的结构,这样开发人员不必从头设计创建和管理一个标准Windows应用程序所需的程序,而是“站在巨人肩膀上”,从一个比较高的起点编程,故节省了大量的时间;

         其次,它提供了大量的代码,指导用户编程时实现某些技术和功能。  对用户来说,用MFC开发的最终应用程序具有标准的、熟悉的Windows界面,这样的应用程序易学易用;

         另外,新的应用程序还能立即支持所有标准Windows特性,而且是用普通的、明确定义的形式。事实上,也就是在Windows应用程序界面基础上定义了一种新的标准——MFC标准。

        一、MFC类库概念和组成

         

           类库是一个可以在应用中使用的相互关联的C++类的集合。Microsoft提供了一个基础类库MFC,其中包含用来开发C++和C++

Windows应用程序的一组类。基础类库的核心是以C++形式封装了大部分的Windows API。类库表示窗口、对话框、设备上下文、公共GDI对象如画笔、调色板、控制框和其他标准的Windows部件。这些类提供了一个面向Windows中结构的简单的C++成员函数的接口。   MFC可分为两个主要部分:
(1)基础类(2)宏和全程函数。 

        1、MFC基础类

         MFC中的类按功能来分可划分为以下几类:
        基类 应用程序框架类 应用程序类 命令相关类 文档/视类
        线程类 可视对象类 窗口类 视类 对话框类
        属性表 控制类 菜单类 设备描述表 绘画对象类
        通用类 文件 诊断 异常 收集
       模板收集 其他支持类      OLE2类 OLE基类 OLE可视编辑包装程序类
        OLE可视编辑服务器程序类 OLE数据传输类 OLE对话框类 其他OLE类 数据库类
        2、宏和全局函数

        若某个函数或变量不是某个类的一个成员,那么它是一个全程函数或变量。Microsoft基本宏和全程函数提供以下功能:

        数据类型运行时刻对象类型服务诊断服务异常处理
        CString格式化及信息框显示消息映射应用消息和管理对象连接和嵌入(OLE)服务
        标准命令和Windows IDs  
       3、约定

       全程函数以“Afx”为前缀,所有全程变量都是以“afx”为前缀,宏不带任何特别前缀,但是全部大写。 常见的全局函数和宏有:AfxGetApp,AfxGetMainWnd,AfxMessageBox,DEBUG_NEW等,我们会在后面的章节中用到并对它们进行介绍。

         从继承关系来看,又可将MFC中的类分成两大类:大多数的MFC类是从CObject继承下来;另外一些类则不是从CObject类继承下来,这些类包括:字符串类CString,日期时间类CTime,矩形类CRect,点CPoint等,它们提供程序辅助功能。

         由于MFC中大部分类是从CObject继承下来的,CObject类描述了几乎所有的MFC中其他类的一些公共特性,因此我们有必要理解CObject类。   我们首先查看一下CObject类的定义,CObject类定义如下清单2.1所示: 

// class CObject is the root of all compliant objects

#ifdef _AFXDLL
class CObject
#else
class AFX_NOVTABLE CObject
#endif
{
public:

// Object model (types, destruction, allocation)
	virtual CRuntimeClass* GetRuntimeClass() const;
	virtual ~CObject();  // virtual destructors are necessary

	// Diagnostic allocations
	void* PASCAL operator new(size_t nSize);
	void* PASCAL operator new(size_t, void* p);
	void PASCAL operator delete(void* p);
#if _MSC_VER >= 1200
	void PASCAL operator delete(void* p, void* pPlace);
#endif

#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
	// for file name/line number tracking using DEBUG_NEW
	void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#if _MSC_VER >= 1200
	void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);
#endif
#endif

	// Disable the copy constructor and assignment by default so you will get
	//   compiler errors instead of unexpected behaviour if you pass objects
	//   by value or assign objects.
protected:
	CObject();
private:
	CObject(const CObject& objectSrc);              // no implementation
	void operator=(const CObject& objectSrc);       // no implementation

// Attributes
public:
	BOOL IsSerializable() const;
	BOOL IsKindOf(const CRuntimeClass* pClass) const;

// Overridables
	virtual void Serialize(CArchive& ar);

#if defined(_DEBUG) || defined(_AFXDLL)
	// Diagnostic Support
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

// Implementation
public:
	static const AFX_DATA CRuntimeClass classCObject;
#ifdef _AFXDLL
	static CRuntimeClass* PASCAL _GetBaseClass();
#endif
};

      CObject类为派生类提供了下述服务:

      1、对象诊断

       MFC提供了许多诊断特性,它可以:

        输出对象内部信息:CDumpContext类与CObject的成员函数Dump配合,用于在调试程序时输出对象内部数据

        对象有效性检查:重载基类的AssertValid成员函数,可以为派生类的对象提供有效性检查
        运行时访问类的信息:MFC提供了一个非常有用的特性,它可以进行运行时的类型检查。如果从CObject派生出一个类,并使用了以下三个宏(IMPLEMENT_DYNAMIC,IMPLEMENT_DYNCREATE或IMPLEMENT_SERIAL)之一,就可以安全可靠的把通用的CObject指针转化为派生类的指针 。

       比如,我们定义一个主窗口类 :

CMyFrame:public CFrameWnd 
  { 
  ...... 
  } 

然后我们使用这个类: 

  CMyFrame *pFrame=(CMyFrame*)AfxGetMainWnd(); 
  pFrame->DoSomeOperation(); 

  AfxGetMainWnd是一个全局函数,返回指向应用程序的主窗口的指针,类型为CWnd*,因此我们必须对它进行强制类型转换,但我们如何知道是否转换成功了呢?我们可以使用CObject的IsKindOf()成员函数检查pFrame的类型,用法如下:  

ASSERT(pFrame->IsKindOf(RUN_TIMECLASS(CMyFrame))); 

 将上一语句插入到pFrame->DoSomeOperation()之前,就可以在运行时作类型检查,当类型检查失败时,引发一个断言(ASSERT[断言声称]),中断程序执行。
 

  2、对象持续性
        通过与非CObject派生的档案类CArchive相结合,提供将多个不同对象以二进制形式保存到磁盘文件(Serilization)中以及根据磁盘文件中的对象状态数据在内存中重建对象(Deserilization)的功能。
 然而,MFC不仅仅是一个类库,它还提供了一层建立在Windows API的C++封装上的附加应用程序框架。该框架提供了Windows程序需要的多数公共用户界面。   所谓应用程序框架指的是为了生成一般的应用所必须的各种软组件的集成。应用框架是类库的一种超集。一般的类库只是一种可以用来嵌入任何程序中的、提供某些特定功能(如图象处理、串行通信)的孤立的类的集合,但应用框架却定义了应用程序的结构,它的类既相互独立,又相互依赖,形成一个统一的整体,可以用来构造大多数应用程序。中国用户熟悉的Borland
C++的DOS下的Turbo Vision和Windows下OWL(Object Windows Language)都是应用框架的例子。

           二、MFC的优点

          Microsoft MFC具有以下不同于其它类库的优势:
          完全支持Windows所有的函数、控件、消息、GDI基本图形函数,菜单及对话框。类的设计以及同API函数的结合相当合理。
          使用与传统的Windows API同样的命名规则,即匈牙利命名法。
          进行消息处理时,不使用易产生错误的switch/case语句,所有消息映射到类的成员函数,这种直接消息到方法的映射对所有的消息都适用它通过宏来实现消息到成员函数的映射,而且这些函数不必是虚拟的成员函数,这样不需要为消息映射函数生成一个很大的虚拟函数表(V表),节省内存。

          通过发送有关对象信息到文件的能力提供更好的判定支持,也可确认成员变量。
          支持异常错误的处理,减少了程序出错的机会
          运行时确定数据对象的类型。这允许实例化时动态操作各域
          有较少的代码和较快的速度。MFC库只增加了少于40k的目标代码,效率只比传统的C Windows程序低5%。

          可以利用与MFC紧密结合的AppWizard和ClassWizard等工具快速开发出功能强大的应用程序。
          另外,在使用MFC时还允许混合使用传统的函数调用。

          三、MFC对消息的管理

          Windows消息的管理包括消息发送和处理。为了支持消息发送机制,MFC提供了三个函数:SendMessage、PostMessage和SendDlgItemMessage。而消息处理则相对来说显得复杂一些。MFC采用了一种新的机制取代C语言编程时对Windows消息的Switch/Case分支,简化了Windows编程,使程序可读性、可维护性大大提高。

          1、MFC对消息的处理
          MFC采用一种消息映射机制来决定如何处理特定的消息。这种消息映射机制包括一组宏,用于标识消息处理函数、映射类成员函数和对应的消息等。其中,用afx_msg放在函数返回类型前面,用以标记它是一个消息处理成员函数。类若至少包含了一个消息处理函数,那么还需要加上一个DECLARE_MESSAGE_MAP()宏,该宏对程序执行部分所定义的消息映射进行初始化

       下面代码演示消息处理:

 class CMainFrame:CFrameWnd{ 
  public: 
  CMainFrame(); 
  protected: 
  //{{AFX_MSG(CMainFrame) 
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); 
  afx_msg void OnEditCopy(); 
  afx_msg void OnClose(); 
  //}}AFX_MSG 
  DECLARE_MESSAGE_MAP() 
  }; 

           成员函数OnCreate,OnEditCopy,OnClose分别用来处理消息WM_CREATE、ID_EDIT_COPY和WM_CLOSE。其中,WM_CREATE和WM_CLOSE是系统预定义消息,包含在Windows.h中。而ID_EDIT_COPY是菜单Edit->Copy的标识,也就是用户选择Edit->Copy菜单项时产生的消息,一般在资源文件头文件中定义。在类的实现部分给出这三个成员函数的定义,以及特殊的消息映射宏。上面的例子的消息映射宏定义如下:

  BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) 
  ON_WM_CREATE() 
  ON_COMMAND(ID_EDIT_COPY, OnEditCopy) 
  ON_WM_CLOSE() 
  END_MESSAGE_MAP() 

         消息映射宏由BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()。其中,BEGIN_MESSAGE_MAP宏包含两个参数CMainFrame类和CFrameWnd,分别代表当前定义的类和它的父类。在BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间,包含了主窗口要处理的各个Windows消息的入口。在本例中,包含三个消息。其中ON_WM_CREATE被用来指定缺省的成员函数OnCreate与WM_CREATE相对应。在MFC中,包含了大量的预定义消息映射宏,用来指定各种成员函数与各种形如WM_XXXX消息相对应。如ON_WM_CLOSE宏指定了WM_CLOSE消息的处理成员函数为OnClose。这时侯,只需要写出要处理的消息就够了,不必再写出处理函数。消息映射宏ON_COMMAND则被用来将菜单项和用户自定义的命令同它们的处理成员函数联系起来。在上例中,用户选择Edit->Copy菜单项时,系统执行OnEditCopy()函数。ON_COMMAND宏的一般定义形式如下:

  ON_COMMAND(command,command_function) 

          其中,command为菜单消息或用户自定义消息,command_function为消息处理函数。MFC允许用户自定义消息,常量WM_USER和第一个消息值相对应,用户必须为自己的消息定义相对于WM_USER的偏移值,偏移范围在0~0x3FFF之间,这对绝大多数程序来说都是够用的。用户可以利用#define语句直接定义自己的消息:  

  #define WM_USER1 (WM_USER+0) 
  #define WM_USER2 (WM_USER+1) 
  #define WM_USER3 (WM_USER+2) 

      为了说明如何使用用户自定义消息,我们看一个例子:

#define CM_APPLE (WM_USER+0) 
  #define CM_ORANGE (WM_USER+1) 
  class CMainFrame:CFrameWnd{ 
  public: 
  CMainFrame(); 
  protected: 
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); 
  afx_msg void OnClose(); 
  //handle user select apple 
  afx_msg LRESULT CMApple(WPARAM wParam, LPARAM lParam); 
  //handle user select orange 
  afx_msg LRESULT CMOrange(WPARAM wParam, LPARAM lParam); 
  DECLARE_MESSAGE_MAP() 
  }; 
  //相应的消息映射如下: 
  BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) 
  ON_WM_CREATE() 
  ON_MESSAGE(CM_APPLE, CMApple) 
  ON_MESSAGE(CM_ORANGE,CMOrange) 
  ON_WM_CLOSE() 
  END_MESSAGE_MAP() 

       第一个ON_MESSAGE宏用于指定 CM_APPLE 命令消息的处理成员函数为CMApple,而第二个ON_MESSAGE宏用于指定CM_ORANGE命令消息的处理函数为CMOrange。
      2、消息的发送
       Windows应用程序允许应用程序向自己发送消息、向其他应用程序发送消息,甚至可以向Windows操作系统本身发送消息(比如要求关闭操作系统或重新启动操作系统)。Windows提供了三个API函数用于发送消息,这三个函数是:SendMessage、PostMessage和SendDlgItemMessage。

         SendMessage用于向窗口发送消息
,该函数说明如下: 

 LRESULT SendMessage(HWND hWnd, //消息要发往的窗口的句柄 
  UINT Msg, //要发送的消息 
  WPARAM wParam, //消息的第一个参数 
  LPARAM lParam //消息的第二个参数); 

       该函数调用目标窗口的窗口函数,直到目标窗口处理完该消息才返回。

       PostMessage函数同SendMessage类似,它把消息放在指定窗口创建的线程的消息队列中,然后不等消息处理完就返回,而不象SendMessage那样必须等到消息处理完毕才返回。目标窗口通过GetMessage或PeekMessage从消息队列中取出并处理。PostMessage函数说明如下:  

BOOL PostMessage(HWND hWnd, //消息发往的窗口 
  UINT Msg, //要发送的消息 
  WPARAM wParam, //消息的第一个参数 
  LPARAM lParam //消息的第二个参数); 

        SendDlgItemMessage函数用于向对话框的某个控制发送消息,函数声明如下:

LONG SendDlgItemMessage(HWND hDlg, //对话框句柄 
  int nIDDlgItem, //对话框控件的ID 
  UINT Msg, //要发送的消息 
  WPARAM wParam, //消息的第一个参数 
  LPARAM lParam //消息的第二个参数); 

         MFC将这三个函数封装为CWnd类的成员函数,隐藏了窗口句柄和对话框句柄这三个成员函数用于向本窗口发送消息,函数的说明如下:  

 LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); 
  BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); 
  LRESULT SendDlgItemMessage( int nID, UINT message, WPARAM wParam=0, LPARAM lParam=0); 

        四、学习MFC的方法
         首先要对Windows API有一定的了解,否则无法深入学习MFC。至少要知道Windows对程序员来说意味着什么,它能完成什么工作,它的一些常用数据结构等。   另一点是不要过分依赖于Wizards。Wizards能做许多工作,但同时掩饰了太多的细节。应当看看AppWizard和ClassWizard为你所做的工作。在mainfrm.cpp中运行调试器来观察一下MFC运行的流程。除非你理解了生成的代码的含义,否则无法了解程序是如何运行。  

        五、应用程序执行机制

        1、WinMain函数

         在DOS下,程序的执行是从main函数开始的。在Windows下,对应的函数是WinMain。但是,如果浏览Hello程序的所有的方法和全局函数,是找不到WinMain函数的。MFC考虑到典型的Windows程序需要的大部分初始化工作都是标准化的,因此把WinMain函数隐藏在应用程序的框架中,编译时会自动将该函数链接到可执行文件中。程序员可以重写WinMain函数,但一般不需要这么做。

下面的程序清单给出了WinMain函数的代码。其中,_tWinMain函数在/DevStudio/Vc/Mfc/src/AppModul.cpp中定义,它所调用的AfxWinMain函数在同一目录下的WinMain.cpp中定义。名字是_tWinMain函数而不是WinMain,是考虑到对不同字符集的支持,在tchar.h中有_tWinMain的宏定义。在ANSI字符集下编译时,_tWinMain就变成WinMain,在Unicode下编译时,_tWinMain就变成wWinMain。

          提示:Unicode是具有固定宽度、统一的文本和字符的编码标准。由于Unicode采用的是16位编码,因此可以包含世界各地的书写系统的字符和技术符号(如中文也在Unicode之中),从而克服了ASCII码在表示多语言文本上的不足之处,扩大了ASCII码7位编码方案的好处。Unicode同等地对待所有的字符,并且在表示各种语言的任何字符时既不需要换码序列(escape)也不需要控制代码。Win32和Visual C++很好的支持Unicode字符集。

      tWinMain函数定义:

 // export WinMain to force(强制) linkage(联接) to this module 
  extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  LPTSTR lpCmdLine, int nCmdShow); 
  #ifdef _MAC 
  extern "C" int PASCAL 
  #else 
  extern "C" int WINAPI 
  #endif 
  _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int 
nCmdShow) 
  { 
  // call shared/exported WinMain 
  return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 
  } 
  AfxWinMain函数定义: 
  // Standard WinMain implementation(执行) 
  // Can be replaced as long as 'AfxWinInit' is called first 
  int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  LPTSTR lpCmdLine, int nCmdShow) 
  { 
  ASSERT(hPrevInstance == NULL); 
      int nReturnCode = -1; 
  CWinApp* pApp = AfxGetApp(); 
  // AFX internal initialization 
  if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) 
  goto InitFailure; 
      // App global initializations (rare) 
  ASSERT_VALID(pApp); 
  if (!pApp->InitApplication()) 
  goto InitFailure; 
  ASSERT_VALID(pApp); 
      // Perform specific initializations 
  if (!pApp->InitInstance()) 
  { 
  if (pApp->m_pMainWnd != NULL) 
  { 
  TRACE0("Warning: Destroying non-NULL m_pMainWnd/n"); 
  pApp->m_pMainWnd->DestroyWindow(); 
  } 
  nReturnCode = pApp->ExitInstance(); 
  goto InitFailure; 
  } 
  ASSERT_VALID(pApp); 
      nReturnCode = pApp->Run(); 
  ASSERT_VALID(pApp); 
  InitFailure: 
  #ifdef _DEBUG 
  // Check for missing AfxLockTempMap calls 
  if (AfxGetModuleThreadState()->m_nTempMapLock != 0) 
  { 
  TRACE1("Warning: Temp map lock count non-zero (%ld)./n", 
  AfxGetModuleThreadState()->m_nTempMapLock); 
  } 
  AfxLockTempMaps(); 
  AfxUnlockTempMaps(-1); 
  #endif 
  AfxWinTerm(); 
  return nReturnCode; 
  } 

         应用程序执行时,Windows自动调用应用程序框架内部的WinMain函数,WinMain函数会查找该应用程序的一个全局构造对象,这个对象是由CWinApp派生类构造的,有且只有一个。它是一个全局对象,因此在程序启动时,它就已经被构造好了。   随后,WinMain将调用这个对象的InitApplication和InitInstance成员函数,完成应用程序实例的初始化工作。随后,WinMain调用Run成员函数,运行应用程序的消息循环。在程序结束时,WinMain调用AfxWinTerm函数,做一些清理工作。
 
        2、应用程序类

       每个应用程序必须从CWinApp派生出自己的应用程序类,并定义一个全局的对象。该应用程序类包含了Windows下应用程序的初始化、运行和结束过程。基于框架建立的应用程序必须有一个(且只能有一个)从CWinApp派生的类的对象。在Hello程序中,我们从CWinApp中派生出一个CHelloApp类,并定义了一个全局对象theApp。CHelloApp类在hello.cpp中定义。  要访问应用程序类构造的对象,可以调用全局函数AfxGetApp()。AfxGetApp()返回一个指向全局对象的指针。可以通过对它进行强制类型转换,转换为我们派生的应用程序类。
 比如:CHelloApp* pApp=(CHelloApp*)AfxGetApp();

       3、框架窗口

       框架窗口为应用程序的用户界面提供结构框架,它是应用程序的主窗口,负责管理其包容的窗口,一个应用程序的最顶层的框架窗口是应用程序启动时创建的第一个窗口。 MFC提供三种类型的框架窗口:单文档窗口,多文档窗口(MDI),对话框。MFC单文档窗口一次只能打开一个文档框架窗口,而MDI应用程序运行时,在应用程序的一个实例中打开多个文档框架窗口,这些窗口称作子窗口(Child Window)。这些文档可以是同一类型的,也可以是不同类型的。如Visual Studio就可以打开资源文件窗口和源程序窗口等不同类型的窗口。此时,激活不同类型的MDI子窗口,菜单也将相应变化。

         MFC提供了三个类CFrameWnd、CMDIFrameWnd、CMDIChildWnd和CDialog 分别用于支持单文档窗口、多文档窗口和对话框。 

        CFrameWnd : 用于SDI框架窗口,形成单个文档及其视的边框。框架窗口既是应用程序的主框架窗口,也是当前文档对应的视图的边框。

      CMDIFrameWnd :  用于MDI应用程序的主框架窗口。主框架窗口是所有MDI文档窗口的容器,并与它们共享菜单条。MDI框架窗口是出现在桌面中的顶层窗口。

      CMDIChildWnd :  用于在MDI主框架窗口中显示打开的各个文档。每个文档及其视图都有一个MDI子框架窗口,子框架窗口包含在MDI主框架窗口中。子框架窗口看起来类似一般的框架边框窗口,但它是包含在主框架窗口中,而不是位于桌面的,并且为主窗口所裁剪。而且MDI子窗口没有自己的菜单,它与主MDI框架窗口共享菜单。

      CDialog :  对话框是一种特殊类型的窗口,它的边框一般不可以调整,而且内部包含一些控件窗口。
      要生成一个单文档窗口,主窗口就必须从CFrameWnd派生;要生成一个多文档窗口,主窗口就必须从CMDIFrameWnd派生,而且其中的子窗口必须从CMDIChildWnd派生出来;而基于对话框的窗口程序就要从CDialog派生出主窗口类。
 

.......................................................................................................................................................................................................................

.......................................................................................................................................................................................................................

本文来自CSDN博客:http://blog.csdn.net/Tunix126/archive/2006/11/14/1384121.aspx

        

抱歉!评论已关闭.