MFC应用程序框架入门(转)

1 MFC概述

  顾名思意,MFC应用程序框架是以MFC作为框架基础的,以此程序框架模式搭建起来的应用程序在程序结构组织上是完全不同于以前的Win32 SDK编程方式的。自20世纪90年代初问世以来,MFC一直试图把Windows API函数封装到类库中个各个逻辑类中。MFC的这种封装并非简单地对API函数进行分组与打包,而是更多地通过类来试图实现全部的系统策略。随着越来越多系统功能的加入,MFC的规模也在不断拓展,目前已包括有200多个类,涵盖了通用Windows 类、文档/视框架、OLE、数据库、Internet以及分布式功能等多方面的基本内容。这样一个坚实的程序开发基础无疑从很大程度上方便了程序设计人员对Windows 程序的开发。

  MFC提供了相当多不同功能的类以适合尽可能广泛的需求。这里绝大多数的MFC类都是直接或间接从CObject类派生出来的,CObject类为其派生类提供了三个重要的特性支持:持久性(Serialization)支持、运行时(Run-time)类信息支持和诊断(Diagnostic)调试支持等。其中持久性是以流的方式将某个类对象中的持久性数据输出或输入到外部存储介质如磁盘文件等的过程;运行时类信息(Run-time Class Information,RTCI)则可以重新获取一个对象的类名及其他一些有关对象在运行时的信息。RTCI也是C++中除运行时类型信息(Run-time Type Information,RTTI)机制外的另一个重要工具;诊断和调试支持作为CObject类的一个组成部分,可以在实现CObject派生类时执行有效性检查并可向调试窗口输出状态信息。

  并非MFC提供的所有函数都是类成员函数,MFC也提供了一系列以Afx为前缀的全局函数。类成员函数只能在其所属类对象所在的上下文中使用,但是这些AFX函数却可以在任何时候的任何地方直接使用。下表列出的是几个比较重要AFX函数:

函数名 函数说明
AfxAbout 无条件终止一个应用程序;通常在发生无法回复的错误时使用
AfxBeginThread 创建一个新的线程并开始执行
AfxEndThread 终止当前正在执行的线程
AfxMessageBox 显示一个Windows 消息窗口
AfxGetApp 返回一个指向应用程序对象的指针
AfxGetAppName 返回应用程序名
AfxGetMainWnd 返回一个指向应用程序主窗口的指针
AfxGetInstanceHandle 返回一个标识当前应用程序实例的句柄
AfxRegisterWndClass 为一个MFC应用程序注册一个用户自定义的窗口类

2 MFC对API函数的封装

  如果读者曾经有过SDK的开发经历,一定会对其烦琐的编程方式和大量的Win32 API函数调用深有感触。所有不同功能的API函数均是以全局函数的形式放在一起的,由于API函数数目比较庞大,因此无论是学习还是使用都是有一定难度的。相比而言,建立在API函数基础之上的MFC类库则通过把相关API函数的分类封装而可以大大简化编程的难度,用MFC类编写的Windows 应用程序完成相同的任务只需要进行少量的工作。

  众多的API函数根据功能的不同而被MFC封装到200多个类中,这些类基本涵盖了进行Windows 编程大部分可能用到的功能。由于封装后的MFC类太多,这里不能一一介绍,下面就以其中比较重要的CObject类和CWnd类为例对API函数的封装情况做一简要介绍。

  CObject类是MFC中最主要也是最基本的类之一,该类不支持多重继承,派生的类只能有一个CObject基类。CObject类是位于类层次结构最顶层的,绝大多数MFC类都是从CObject类派生出来的。CObject类包含了所有MFC类必须具备的几个基本功能:持久性支持、运行时类信息支持和诊断调试支持。其中持久性支持功能由成员函数IsSerializable()和Serialize()提供。前者用于检测对象是否支持序列化。如果一个类能够被序列化,就必须在声明时包含DECLARE_SERIAL宏、在实现时包含IMPLEMENT_SERIAL宏。Serialize()函数则可以将对象写入档案文件(Archive)或从档案文件读出对象。成员函数GetRuntimeClass()可以获取到一个指向CruntimeClass类对象的指针,通过该指针可以得到对象的运行时类信息。CObject类在诊断调试支持方面提供了成员函数AssertValid()和Dump(),前者可对对象内存状态的有效性进行检查,后者负责将对象的内容转储到一个CdumpContext对象中,并可以提供诊断服务及一些有用的调试信息。

  在MFC中,CWnd类提供了所有窗口类的基本功能,是一个非常重要的类,大约三分之一的MFC类都是以此为基类。该类主要对创建、操纵窗口类的API函数进行了封装,而且通过消息映射机制隐藏了SDK编程中使用相当不便的窗口处理函数,是消息的分发处理更加方便。

  CWnd类最重要的一个封装是对API函数CreateWindow()的封装,该函数被封装为CWnd类成员函数Create()。从VC提供的MFC源文件WinCore.cpp中可以清楚看出CWnd类对CreateWindow()函数的封装过程,下面给出相关部分的实现清单:

BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
// can‘t use for desktop or pop-up windows (use CreateEx instead)
ASSERT(pParentWnd != NULL);
ASSERT((dwStyle & WS_POPUP) == 0);
return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);
}

  可以看出,主要工作是在CreateEx()成员函数中完成的,而该函数又是对API函数CreateWindowEx()的封装。封装后的代码在调用CreateWindowEx()前构造并填充了一个非常类似于WNDCLASS结构的CREATESTRUCT结构,并调用了PreCreateWindow()。

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
#ifdef _DEBUG
if (hWnd == NULL)
{
TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n", GetLastError());
}
#endif
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
if (hWnd == NULL)
return FALSE;
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
return TRUE;
}

  看上去经过封装的窗口创建函数要比原API函数复杂许多,但这并不说明MFC的封装将导致编程的效率低下,恰恰相反,由于CWnd在绝大多数场合中是以基类的形式出现的,因此可在派生类中添加代码完成对CWnd::Create()的调用而比较方便的实现对派生类窗口的创建。

3 MFC应用程序框架

  MFC应用程序框架可以看作是MFC基本类库的一个超集(Superset),类库是众多可在任何程序中使用的类的集合,而应用程序框架则定义了程序自身的结构。下面给出一个使用了MFC应用程序框架的简单例子,通过这段例程可以比较清楚地了解MFC应用程序框架的一般结构。

// Sample01.h文件
// 应用程序类
class CSample01App : public CWinApp
{
public:
virtual BOOL InitInstance();
};
// 框架窗口类
class CSample01Frame : public CFrameWnd
{
public:
CSample01Frame();
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
};
// Sample01.cpp文件
#include <afxwin.h>
#include "Sample01.h"
// 应用程序对象
CSample01App theApp; 
// 初始化应用程序实例
BOOL CSample01App::InitInstance()
{
m_pMainWnd = new CSample01Frame();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// 消息映射 
BEGIN_MESSAGE_MAP(CSample01Frame, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// 构造函数
CSample01Frame::CSample01Frame()
{
Create(NULL, "MFC应用程序框架程序");
}
// WM_PAINT消息响应函数
void CSample01Frame::OnPaint()
{
CPaintDC dc(this);
dc.TextOut(100, 100, "Hello World!");
}

  仍象编写Sample00程序一样建立一个Win32应用程序工程Sample01(配套程序见"光盘\配套程序\Sample01\"),然后分别向工程添加头文件Sample01.h和源文件Sample01.cpp,并将上述代码写入相应的文件。为了能顺利编译,还需要修改一下编译命令,通过"Alt+F7"快捷键呼出【Project Settings】对话框,在【Preprocessor definitions】栏的最后添加选项"_AFXDLL",前面用逗号分隔。接下来还需要在【Project Options】栏的最后添加命令行"/MD",用空格同其他命令行参数进行分隔。编译运行,可以看出效果同SDK方式编写的Sample00程序是一样的,但在代码实现上更加结构化,编写过程也更加简单。

  接下来,对上述应用程序框架代码进行分析。首先从MFC应用程序的核心--CWinApp类的派生类CSample01App谈起。CWinApp类提供了可以获取消息并将获取到的消息分发到应用程序窗口的消息循环和一些关键的虚函数,通过对这些虚函数的重载可使开发人员对应用程序的一些固有行为进行扩展。当把头文件Afxwin.h包含进来后,就可以在程序中使用包括CWinApp在内的一些MFC类了。一个MFC应用程序有且只能有一个应用程序对象而且必须被以全局方式进行声明,所以该对象自程序开始运行起就一直驻留在内存。

  由于使用了MFC应用程序框架的程序在本质上仍是Windows 应用程序,因此必然需要在程序中存在作为Windows 应用程序入口的WinMain()函数。在前面的示例代码中之所以没有看到WinMain()函数是由于该函数已经通过封装的手段隐藏到应用程序框架中了。除WinMain()外,CWinApp类成员函数Run()也是隐含执行的,这个函数也是非常重要的,它负责把消息放进应用程序窗口的消息循环中,由WinMain()函数完成对Run()的调用 。当WinMain()函数寻找到应用程序对象后将立即调用CWinApp类的虚函数InitInstance()。由于CWinApp基类是不知道究竟需要何种主框架窗口的,因此在使用时必须在CWinApp的派生类中对InitInstance()函数进行重载。InitInstance()函数是在应用程序已经开始运行但窗口尚未创建时被调用的,若非由InitInstance()创建了窗口,应用程序是无法拥有窗口的,这也就意味着缺少了InitInstance()函数的应用程序将无法接收、处理消息,对Windows程序而言这也就失去了存在的意义。由此可见,从CWinApp类中进行派生,并且对InitInstance()函数进行重载是编写MFC应用程序框架程序的必要条件。

  除应用程序类外,从CFrameWnd派生的CSample01Frame类还对应用程序的主框架窗口做了描述。在构造函数中调用基类的CFrameWnd成员函数Create(),由Windows 负责创建出实际的窗口结构,并由应用程序框架将其链接到C++对象。

  本示例程序的大部分功能实际是在MFC的CWinApp和CFrameWnd等基类中完成的,在编程时,只需在派生类中编写少量功能代码,C++允许以这样的方式借用基类中的大量代码而无须复制代码。应用程序框架负责提供程序的结构框架,开发人员在此基础上为其添加相应的实现代码,从而可以非常方便地完成一个完整的应用程序。应用程序框架不仅定义了应用程序的结构安排,实际上还包含了更多的C++基类。

  4 小结

  SDK的API编程方法、MFC的编程方法以及本系列讲座后面将要介绍的ATL编程方法是VC++程序设计中比较常用的几种编程方法,其中MFC以其强大的功能和灵活的编程方式而成为大多数程序开发人员最经常使用的一种编程方式。本文从基础问题入手对MFC及其框架程序做了较为详细的论述,使读者能够对MFC编程有一个基本的认识。

转自:http://www.cnblogs.com/lidabo/archive/2012/11/23/2785030.html

时间: 2024-10-09 17:30:00

MFC应用程序框架入门(转)的相关文章

VS2010/MFC编程入门之四(MFC应用程序框架分析)

VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用程序工程中文件的组成结构,可能大家对工程的运行原理还是很模糊,理不出头绪,毕竟跟C++编程入门系列中的例程差别太大.这一节鸡啄米就为大家分析下MFC应用程序框架的运行流程. 一.SDK应用程序与MFC应用程序运行过程的对比        程序运行都要有入口函数,在之前的C++教程中都是main函数,

鸡啄米MFC教程笔记之二:MFC应用程序框架分析

这一节鸡啄米就为大家分析下MFC应用程序框架的运行流程. 一.SDK应用程序与MFC应用程序运行过程的对比 程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数,MFC程序也是从WinMain函数开始的.下面鸡啄米就给出用Windows SDK写的“HelloWorld”程序,与MFC应用程序框架进行对比,这样能更好的了解框架是怎样运行的.Windows SDK开发程序就是不使用MFC类库,直接用Windows API函数进行软件开

MFC应用程序框架(转)

对于程序员来说,如果要提高编程效率,一个好用的,功能强大的并且可以得心应手使用的编程工具往往会给我们程序员带来莫大的方便.其实对于现在的编程工具来说,使用哪一种工具都不是问题的关键,重要的是你能够使用到什么程度,毕竟现在的工具都是非常的强大,对于一般的编程任务来说还没有不能够胜任的工具,否则的话恐怕他就不可能在这个世界上存在哪怕是只有一个月的生命. 但是根据个人所好以及周围的人的影响,我们都会去使用某一种或者几种工具.比较Visual Basic .C++ Builder和Delphi等编程工具

【MFC】程序框架及基础知识

1. 首先,贴一个简单的Win32的Hello World程序,这是学MFC的基础. 如果没有学过Win32,请自行补充相关知识. #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { static TCH

第四课——MFC应用程序框架

一.MFC应用程序类型 上篇文章的彩蛋:可通过使用MFC应用程序向导(MFC AppWizard)的功能来创建所需要的应用程序,这意味着不需要输入任何代码.MFC除了应用程序向导,还对应用程序项目有着独特的管理方式. 1. MFC AppWizard 作用:能为用户生成一些常用的标准程序结构和编程风格,它们被称为应用程序框架结构. 提供的应用程序框架类型:包括MFC AppWizard(dll).MFC AppWizard(exe).Win32 Application等,这些类型基本上能满足各个

windows应用程序框架及实例

应用程序框架:同一类型应用程序的结构大致相同,并有很多相同的源代码,因此可以通过一个应用程序框架AFX(Application FrameWorks)编写同一类型应用程序的通用源代码. 主要向导: Datebase Project:创建数据库项目 MFC ActiveX Control Wizard:创建基于MFC的ActiveX控件 MFC AppWizard[dll]:创建基于MFC的动态链接库 MFC AppWizard[exe]:创建基于MFC的应用程序(最常用) New Databas

(转载)VS2010/MFC编程入门之五十二(Ribbon界面开发:创建Ribbon样式的应用程序框架)

上一节中鸡啄米讲了GDI对象之画刷CBrush,至此图形图像的入门知识就讲完了.从本节开始鸡啄米将为大家带来Ribbon界面开发的有关内容.本文先来说说如何创建Ribbon样式的应用程序框架. Ribbon界面就是微软从Office2007开始引入的一种为了使应用程序的功能更加易于发现和使用.减少了点击鼠标的次数的新型界面,从实际效果来看,不仅外观漂亮,而且功能直观,用户操作简洁方便. 鸡啄米将以图文结合的方式来说明利用MFC向导创建Ribbon样式的单文档应用程序框架的过程,其实Ribbon应

VS2010/MFC编程入门之五十二(Ribbon界面开发:创建Ribbon样式的应用程序框架)

上一节中鸡啄米讲了GDI对象之画刷CBrush,至此图形图像的入门知识就讲完了.从本节开始鸡啄米将为大家带来Ribbon界面开发的有关内容.本文先来说说如何创建Ribbon样式的应用程序框架. Ribbon界面就是微软从Office2007开始引入的一种为了使应用程序的功能更加易于发现和使用.减少了点击鼠标的次数的新型界面,从实际效果来看,不仅外观漂亮,而且功能直观,用户操作简洁方便. 鸡啄米将以图文结合的方式来说明利用MFC向导创建Ribbon样式的单文档应用程序框架的过程,其实Ribbon应

美团小程序框架mpvue入门教程

自打写了 美团小程序框架mpvue蹲坑指南, 一发不可收拾,今天趁周末空闲,来写个mpvue(没朋友)的简单入门教程,本教程只针对新手,老鸟勿喷. 另外,我还专门为本文做了一个简单的项目,如果懒得从头开始搭项目的童鞋,可以直接去我的 github上克隆到本地, 安装一下依赖,即可直接在此基础在开发,不需要做任何配置.如果你觉得该项目对有帮助, 请顺便给我Star,你们的支持是我最大的动力,谢谢! 好了,我们进入主题,首先,请允许引用一下美团官方对mpvue的介绍 mpvue是一个使用 Vue.j