烦恼一般都是想太多了。

0%

MFC基础概念及基本类

Windows-程序工作过程 一问中我们介绍了 Windows 程序的基本工作过程,那么在 MFC 中其又是怎么样工作的呢。

简介

MFC 是微软基础类库,是对 windows SDK API 以面向对象的形式进行了封装。将一些模板化的,常规化的代码进行了封装后,我们就可以省却很多事情了。我们从一个最简单的基于对话框的程序来看。

对于使用了 winapi 来的程序来说,其本来是过程式的,但是使用 MFC 将一些功能,模块用对象的形式进行封装,简化了过程,也更加容易写出代码。

模块、进程、线程

首先我们来先了解一下这三个概念。

  • 模块:表示的是一个可执行程序(.exe)由 Windows 操作系统加载在内存中的一种表示
  • 进程:操作系统为程序所分配的一系列权限,内存,资源等的集合。
  • 线程:表示的是程序的一个执行路径。

对于 MFC 程序来说,操作系统在加载程序的时候,就会自动的建立一个模块,进程,一个线程。而我们后面所有的东西,都是在后面这么一个线程来开始的。

基本类

CObject

所有 MFC 类的基类,所有的类都继承自这个类。其最重要的一点就是支持序列化。

CCmdTarget

此类是所有消息映射体系结构的基类。所有响应事件或消息的类都应该从这个类继承。如 CWinApp, CWnd 等。从其命名上来看表示的是 Cmd Target ,命令目标,也就不难理解了。

官方的备注是:

这是一个消息映射,会将命令和消息路由到我们编写的用来处理这些命令和消息的函数。(命令 指的是一个从一个菜单项,按钮等来的消息)
从 CCmdTarget 继承的关键的框架类包括:CView, CWinApp, CDocument, CWnd, CFrameWnd。如果要构造自己的消息处理的类,那么从这些衍生类进行继承,通常,我们很少会直接继承 CCmdTarget。
CCmdTarget 还包含了显示一个沙漏光标函数。这用来在你要提示用户需要一个较长时间的任务需要执行。

CWinThread

这个我代表了一个应用内的执行线程。

class CWinThread : public CCmdTarget

应用的主线程通常由一个继承自 CWinApp 的类来提供;而 CWinApp 是继承自 CWinThread 的。同时 CWinThread 允许在一个应用内有多个线程。

CWinThread 支持两种类型的线程:工作线程与 UI 线线程。工作线程不会处理消息,UI 线程线程处理来自系统的各种消息。

CWinApp 及其衍生类是 UI 线程的实例。

CWinThread 对象存在于线程的生命周期内。

CWinThread 用来保证我们的 MFC 应用是线程安全的。框架需要用到的线程定信息保存在线程本地数据内,这是由 CWinThread 来管理的。因为依赖于 CWinThread 来管理这些线程本地数据,所以任何使用 MFC 的线程必须通过  MFC 来建立。使用 API 函数 _beginthread, _beginthreadex 建立的线程是不能使用任何 MFC API 的

要建立一个 MFC 线程,使用 AfxBeginThread。它有两种形式,具体是那种依赖于你到底想要的是一个工作线程还是UI线程。如果想要的是一个 UI 线程,那么需要传递给它一个指向 继承自 CWinThread 的类作为参数;如果只是需要一个工作线程,那么将工作函数及工作参数传递过去就行了。AfxBeginThread 会返回一个指向 CWinThread 对象的指针。

当然,我们可以 先构建一个 CWinThread 对象,然后使用 CreateThread 来建立线程。 这样的两步操作在我们重用 CWinThread 的时候就很实用了。

CWinApp

class CWinApp : public CWinThread

MFC 中的主应用程序类将封装 Windows 操作系统的应用程序的初始化、运行和终止。 基于框架的应用程序都必须有且只有一个对象的类派生自CWinApp创建窗口之前将构造此对象。

CWinApp 是一个应用对象,其提供了成员函数来初始化我们的应用及开始运行应用。

这个对象是在其他 C++ 全局对象构建的时候构建的,当 Windows 调用 WinMain 函数的时候它已经是可用的了。必须将我们的 CWinApp 对象声明为全局的。

当我们的继承 CWinApp 的时候,需要重写 InitInstance() 方法来建立我们自己的主窗口对象。。

除来 CWinApp 的成员方法,MFC 提供 一些全局函数来访问我们的 CWinApp 对象和其他的全局对象。

  • AfxGetApp() 获取我们 CWinApp 对象指针。
  • AfxGetInstanceHandle() 获取一个指向我们当前应用实例的 Handle。
  • AfxGetResourceHandle() 获取一个指向我们当前应用实例的资源 Handle。
  • AfxGetAppName() 获取一个指向我们的应用名称的字符串的指针。如果我们已经有了一个指向 CWinApp 对象的指针,我们可以用 m_pszExeName 来获取这个名称。

如果我们想要判断是否已经有一个当前应用的实例在运行,使用一个名称互斥量。如果打开这个互斥量失败,就表示当前没有此应用的实例在运行。

CWnd

MFC 所有窗口的基类。

class CWnd : public CCmdTarget

CWnd 对象与 Windows 的窗口是不一样的,但两者之间有关联。Cwnd 对象的建立和销毁是由其构造器(析构器)完成的。而 Windows 窗口,是 Windows 操作系统的一个数据结构,其由 Create 成员方法来建立,由 Cwnd 的虚 析构器来销毁。DestroyWindow 函数会销毁 Windows 的窗口,但不会销毁 Cwnd 对象本身。

也就是说 Cwnd 对象实际上是持一个 Windows 窗口对象的。

CWnd 类和消息映射机制隐藏了我们的窗口处理函数 WndProc 这么一个事实。由 Windows 发来的消息和通知都由消息映射机制路由到 CWnd 的 On[Message] 函数。我们通过重写 On 方法来处理消息。

CWnd 也允许为我们的应用建立子窗口。

继承 CWnd 来写我们自己的类,然后向其添加成员变量来存储我们应用需要的数据。实现消息控制函数及一个消息映射来决定如何处理消息。

我们用两步来建立一个子窗口。

  1. 调用 CWnd 的 构造器来建立 CWnd 对象。
  2. 调用 Create 方法来建立子窗口,然后把他附到 CWnd 对象。

当用户要销毁窗口的时候,我们可以销毁 CWnd 对象,或者调用其 DestroyWindow 函数来销毁子窗口。

CFrameWnd,CDialog,CView都继承自这个类。而 UI 控制的类如 CButton 可以直接使用。

CDialog

class CDialog : public CWnd

用来在屏幕上显示一个对话框。

有两种类型的对话框:抢占式或非抢占式的。一个抢占式的对话框在应用要继续之前必须关闭;而非强制式的话可以在没关闭的时候让用户依然去看其他事情。

一个 CDialog 对象是一个对话框模板和一个继承自 CDialog 类的结合。使用对话框编辑器来编辑模板并保存到资源内,然后使用类生成向导来建立一个继承自 CDialog 的类。

一个对话框,和其他的 Windows 窗口一样,从操作系统接收消息。在对话框内,我们更有兴趣的是处理由对话框上的控件发出的消息。我们可以在对话框编辑器上的属性面板上对控件指定处理函数,这个会自动的添加消息映射到我们的衍生类中。

当然,我们也可以完全的手动来写消息映射和处理函数。

数据交换和有效性验证映射是在 CWnd::DoDataExchange() 方法内完成的,系统和用户都会不直接的调用 CWnd::UpdateData() 来间接的调用这个方法。

CFile

提供二进制的,不带缓冲的输入输入服务。

TCHAR	szBuffer[256]; 
UINT nActual = 0;
CFile myFile;

if ( myFile.Open( _T("c:\\test\\myfile.dat"), CFile::modeCreate |
CFile::modeReadWrite ) )
{
myFile.Write( szBuffer, sizeof( szBuffer ) );
myFile.Flush();
myFile.Seek( 0, CFile::begin );
nActual = myFile.Read( szBuffer, sizeof( szBuffer ) );
}

## CStdioFile

流式缓冲文件类

CString

CString, CStringA, CStringW 是 CStringT 模板类的特定实现。 CStringT 会根据其支持的字符类型来实现。

CStringW 支持 wchar_t 类型和 Unicode 字符串; CStringA 支持 char 类型,支持多字节或者单字节字符串。
CString 支持 wchar_t,char,这依赖与编译的设置。

#include <atlstr.h>

int main() {
CString aCString = CString(_T("A string"));
_tprintf(_T("%s"), (LPCTSTR) aCString);
}

实例