MFC 架构简介
Microsoft Foundation Class library framework 在提供MVC design pattern 的基础方面做得很合理,它使用各种消息来提供各个部分之间的通信。但是,与任何框架一样,它提供的结构可以提供许多您不必编写的功能以及相当多的限制和约束,如果您的方法与框架不一致。
该框架使用协作实体之间的消息,扩展了 Windows 消息的思想,并使用了 Windows 操作系统的消息基础结构。其中一些合作实体有自己的 Windows 消息泵(模式对话框和线程),而其余的则使用由主线程或 UI 线程提供服务的应用程序消息泵。
发送到应用程序主窗口的消息(例如键盘或鼠标事件或使用PostMessage() 发布的消息)由主窗口线程(也称为主 UI 线程)从 Windows 消息队列中拉出,为消息泵提供服务,然后由 MFC 框架转发给它所属的实体。这就是暂停或等待主 UI 线程的操作使应用程序无响应的原因。主 UI 线程必须不断运行以从 Windows 消息队列中提取 Windows 消息,然后将它们分发到应用程序的其余部分。
MFC 框架通过查看消息映射列表来转发消息,以查找哪个实体或 MFC 类实例正在等待该消息,如果找到,则使用参数调用消息映射条目中指定的 C++ 函数MFC 规范要求。期望任何消息处理函数要么快速完成动作并返回到 MFC 框架的消息调度功能,要么使用线程执行任何需要一些时间的动作然后返回。
MVC design pattern 将应用程序视为由三个协作实体组成,模型实体包含数据,视图实体呈现模型及其数据的视图,控制器实体是 affordance用户操作以更改所查看的内容以及模型及其数据。
MFC 框架体系结构从几个基类开始,然后通过继承进行专门化。例如,有一个通用视图类,它为通用显示提供消息基础结构,然后子类扩展并专门化通用视图类。例如,CView 类专用于 CScrollView 类。
MFC UI 类的主要类型
通常,文档类将处理涉及文档内容更改的消息,而视图类将处理涉及文档内容呈现的消息。文档类负责将所有数据或内容序列化到文件对象或从文件对象序列化。
文档类使用Observer design pattern 让视图知道何时需要更新。文档类有一个视图实例列表,因此当文档或模型或数据发生更改时,所有已注册的视图都会收到一条消息,指示更改并需要更新。
视图类负责将文档数据的视图显示到设备上,例如屏幕上的窗口或打印机。因此,CView 类具有处理通过消息 ID 为 ID_FILE_PRINT 或 ID_FILE_PRINT_PREVIEW 的消息发送的打印请求的函数是正常的。
当文档中的数据发生变化时,可以向文档中注册的所有视图发送一条消息,通知这些视图数据发生了变化,以便视图可以决定是否对视图进行更改。
框架的第三个主要类CWinApp 或应用程序类是文档和视图类的容器和管理器。在 app 对象中,它是应用程序的入口点(也用于 MFC DLL 以及提供 DLL 加载和卸载入口点的构造),是主消息泵的挂钩。 app 类的目的是设置初始环境,然后允许程序员将他们的特定文档和视图对象挂钩到 app 类的 MFC 框架中。
由于CWinApp类是应用程序的容器,它处理ID_FILE_NEW或ID_FILE_OPEN这样的事件是正常的,因为它会导致文档类关闭当前文档并重新初始化应用程序从新文档及其关联视图开始。
我的意见是关于对话框处理和帮助处理被放入CWinApp 类,因为它是最容易放置的地方。通常,关于是一个包含应用程序基本描述的对话框。帮助和关于可能是您无需先启动文档或查看即可访问的内容。
MFC 框架还有其他支持主要 UI 功能的类。自 1990 年代初首次引入 MFC 以来,还添加了其他其他库。
关于消息的一些细节
Windows 消息具有指示消息类型的消息 ID。有不同范围的消息 Id 值分配给不同的目的。一个范围是WM_USER 范围,应用程序可以使用该范围而不必担心使用分配给Windows 消息(如键盘消息)的消息ID。此范围的一部分分配给 MFC 消息(请参阅 WM_USER vs WM_APP 以及 Sending and receiving Windows messages 以及 How to handle a posted, registered windows message within a VCL application? )。
这些消息是使用PostMessage() 或SendMessage() 的标准Windows 消息,带有目标窗口句柄、消息标识符和用于提供附加信息的两个参数的标准Windows 格式。 MFC 框架为各种消息标识符定义了许多不同的 C 预处理器。如果您遵循类派生层次结构,您会发现文档类CDocument 最终派生自CCmdTarget 类,就像视图类CView 或CView 的派生一样。 CCmdTarget 又派生自最基本的 MFC 类 CObject。
大部分消息映射功能似乎来自CCmdTarget 类的功能,该类使用接受消息的方法,检查消息标识符是否在特定对象的CCmdTarget 消息列表中,以及如果不将其传递给链中的下一个 CCmdTarget 对象。所以 MFC 框架使用了一种Strategy Design Pattern,这样消息就会沿着组件链传递,直到找到可以处理消息的对象。
由于 MFC 框架的时代,相当多的暴露部分使用 C 预处理器和宏。您可以检查宏以查看它们在做什么以及它们是如何使用 Visual Studio IDE 实现的。而那些作为模板实现的 MFC 框架部分也可以通过 Visual Studio IDE 轻松读取。宏和模板都在 MFC 包含文件中。
但是,要阅读实际类中的代码,您需要找到 MFC 源代码主体的副本。来自 Microsoft 网站的 Using the MFC Source Files 提供了一个起点,包括从哪里可以找到安装 Visual Studio 的源代码。
Microsoft 基础类 (MFC) 库提供完整源代码
代码。头文件 (.h) 位于 \atlmfc\include 目录中;
实现文件 (.cpp) 位于 \atlmfc\src\mfc 目录中。
我不建议您使用自己的更改来分叉。
您的第三个问题是关于线程的,您描述的方法在 MFC 框架中没有意义。使用 MFC,您通常会使用 AfxBeginThread() 或 AfxBeginThreadEx() 函数创建线程。线程有两种,一种有用户界面,一种没有。
多线程 MFC 应用程序需要修改消息泵是没有意义的。使用PostThreadMessage() 之类的东西创建线程后,您将改为向线程发送消息。线程可以通过调用GetMessage() 或PeekMessage() 函数来接收消息。每个 MFC 线程都有一个消息泵和消息队列,它们在“线程第一次调用用户或 GDI 函数之一时”设置(请参阅PostThreadMessageW function)。
参考文献
Is MFC is based on any design pattern,if so which design pattern?
Windows Messages
SendMessage/PostMessage to a derived CView class not working for MFC application
MFC - How to post a message to all views that were derived from CView class?
Is the 'C' in MVC really necessary?
本文Threads with MFC在代码项目中提供了概述和源示例。