MFC (微软)

微软基础类库（Microsoft Foundation Classes，简称MFC）是一个微软公司提供的类库（class libraries），以C++类的形式封装了Windows API，并且包含一个（也是微软产品的唯一一个）应用程序框架，以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
特性.
Visual C++包含MFC应用程序向导，可用于兼容MFC的应用程序。在ATL程序中也可以手动添加MFC支持。在向导中有各种选项以定制生成的程序的功能，例如界面风格、语种、数据库开发支持、打印支持、自动化支持、ActiveX支持、网络支持、基于HTML的帮助文档支持等等。
在COM开发方面，相对于ATL来说，MFC的组件比较大，代码不够短小精悍，但是支持的功能也比较多，例如有对ActiveX Document的封装类。
在界面开发方面，MFC提供对消息循环的封装，使用消息映射来避免虚函数的开销。MFC也提供常用Windows通用控件的封装类。
MFC扩展DLL的接口使得MFC程序可以直接调用MFC扩展DLL中的MFC类。MFC也支持在标准DLL中被使用。
发展.
MFC是在1992年随微软的Microsoft C/C++ 7.0编译器发布的，用于面向16位元Windows的软件开发。起初，MFC是作为一个应用程序框架开发的，所以定名为Application Framework eXtensions（AFX）。
随着.NET框架的发布，曾经一度被微软重点推荐的MFC被Visual Basic .NET、C#、Windows Forms抢走了不少市场份额，但是MFC继续在非托管软件开发中占据重要地位。在托管开发方面，MFC中也包括对Windows Forms和托管／非托管互操作的封装。微软在Windows Vista和Windows 7发布之后在MFC中增加了对新的Windows API支持。
MFC的优点.
MFC的主要优点是可以用面向对象的方法来调用Windows API，以及能够更加便捷地开发应用程序。MFC将很多应用程序开发中常用的功能自动化，并且提供了文档框架视图结构和活动文档这样的便于自定义的应用程序框架。同时，在Visual C++内部也内建了很多对MFC的例如类向导这样的支持以减少软件开发的时间，使用类向导可以快速生成Hello World程序。
MFC的缺点.
虽然MFC的源代码对用户是完全开放的，但是MFC的一些封装过程过于复杂，以致于新用户很难迅速掌握MFC的应用程序框架，以及在调试中定位问题的位置。同时，很多MFC对象不是线程安全的，致使在跨线程访问MFC对象时需要编写额外的代码。另外，MFC的很多类依赖于应用程序向导生成的代码，使得在使用Visual C++中其他类型的应用程序向导生成的工程中添加MFC支持的难度大大增加。
第三方支持.
很多商用类库在MFC的基础上进一步实现了皮肤、渐变风格、多顶层窗口程序、属性列表等较受欢迎的功能；同时，在C++在线社区中，很大一部分开放的源代码也是基于MFC的。
MFC的结构.
作为一个应用程序的开发框架，必须满足各方面的功能需求。
应用程序启动.
基于MFC开发的应用程序在启动时，Windows操作系统：
消息循环与消息映射.
控件的通知消息与反射消息.
窗体上的控件，应当向父窗体通报控件发生的各种事件，如被点击、绘制、内容改变等等，称为通知消息（notification message ）。在 Windows 3.x的16位程序设计时代，控件向父窗体发送WM_COMMAND消息，由父窗体的代码负责实现这些事件。其中wParam的低16位是 control ID，高16位是notification code (例如BN_CLICKED)；lParam是控件句柄。因此，再无可能传递其它信息给父窗体。为此，为传递具有特别内容的控件事件，Windows 3.x定义了一批特殊的通知消息(notification messages)：
早于4.0版本的MFC，在控件类提供了虚函数处理这些通知消息，这一办法已经被下述的“消息反射”取代（但仍然向后兼容继续支持）。
随着Windows 95开始了32位程序时代，伴之而来的是Win32 API 与 MFC 4.0。 Win32增加了很多复杂的控件，需要使用更多的通知消息传递很多复杂的数据给父窗体。Win32 API仅仅增加了一个消息WM_NOTIFY，就实现了这些功能。lParam参数开头是NMHDR数据结构，其后是与该通知类型相关的特定数据结构。
<syntaxhighlight lang="cpp">
typedef struct tagNMHDR {
} NMHDR;
</syntaxhighlight> 
CWnd::OnNotify函数处理通知消息。它的默认实现是检查消息映射表（message map）查找通知的处理器函数并调用。一般说来，不必覆盖OnNotify；而应该写一个处理器函数并增加为该窗口类的消息映射表条目。
成员函数应该写为：
MFC 4.0提供了一种特性“消息反射”（message reflection），允许控件通知消息既可以在父窗口中，也可以在控件中被处理。可以对控件创建一个派生的控件类，实现对从父窗口反射回来的指定类型消息的处理。消息反射是MFC而不是Win32的特性，因此父窗口的类必须是从CWnd派生，从而父窗口在CWnd::OnNotify函数中处理控件的WM_NOTIFY时，首先调用CWnd::ReflectLastMsg把消息反射回控件的CWnd::SendChildNotifyLastMsg函数去处理；ReflectLastMsg返回值就是在控件的消息映射中使用ON_NOTIFY_REFLECT_EX()声明的反射消息处理函数的返回值，可以通知父窗口该消息是否已经被控件处理。控件的CWnd::SendChildNotifyLastMsg函数，首先获得线程的最后一条message，然后调用发送窗口的虚函数OnChildNotify函数。在子窗口处理反射回来的控件消息，第一种方法是重载控件窗口的OnChildNotify虚函数；第二种办法是由CWnd::OnChildNotify默认处理去调用CWnd::ReflectChildNotify函数，进入控件子窗口的MFC消息映射的标准处理（子窗口处理的消息被译成WM_REFLECT_BASE+WM_NOTIFY消息）。对于WM_NOTIFY，仅当在控件的消息映射（message map）中，控件没有通过ON_NOTIFY_REFLECT()声明的反射消息处理函数，父窗口的相应的通知消息处理函数才会被调用（在父窗口消息映射中使用宏ON_NOTIFY声明）。控件中通过ON_NOTIFY_REFLECT_EX()声明的反射消息处理函数可以返回真或假，以决定父窗口是否继续处理该通知消息。WM_NOTIFY以外的其它通知消息，父窗口在第一时间有机会处理它，控件对它的处理排在第二位。反射消息处理函数通常使用特定的名字，对应的消息反射宏的名字是在消息名字加上前缀ON_，后缀_REFLECT。如WM_CTLCOLOR对应ON_WM_CTLCOLOR_REFLECT。但以下三种情况，反射消息处理函数可以随意自行起名，对应的消息反射宏的名字分别为：
消息传递处理机制.
MFC类体系中，Windows消息传递处理机制是基于CCmdTarget类及其派生类的静态成员函数GetThisMessageMap()内部定义的静态数据成员：
在头文件的类定义中使用宏DECLARE_MESSAGE_MAP()来声明静态成员函数GetThisMessageMap与虚函数GetMessageMap
用户所写的类的Windows消息处理函数（例如OnCommand）必须转换为CCmdTarget::*的成员函数指针类型AFX_PMSG，保存在该类的_messageEntries数组中。
<syntaxhighlight lang="cpp">
struct AFX_MSGMAP_ENTRY
 UINT nMessage; // windows消息代号
 UINT nCode; // WM_NOTIFY的控制代码
 UINT nID; // WM_COMMAND下面的ID号，如果为其他的消息，则这个数字为0
 UINT nLastID; //和前面的ID一起组成一个范围，用于发送一次消息，处理执行多次
 UINT nSig; // 标志消息处理函数的类型
 AFX_PMSG pfn; // 函数调用指针
typedef void (CCmdTarget::*AFX_PMSG)(void);</syntaxhighlight>
调用用户类中该消息处理函数时，根据该函数保存在_messageEntries中的signature（一个无符号整型表示的函数的形参类型列表与返回值类型），把类型为void (CCmdTarget::*AFX_PMSG)(void)的成员函数指针强制转为其它类型的CCmdTarget成员函数指针（例如void (AFX_MSG_CALL CWnd::*pfn_v_i_i)(int, int)，目前在union MessageMapFunctions中列出了近百种CCmdTarget成员函数指针），然后调用转换后的成员函数指针。这是基于Visual C++编译器把单继承的成员函数指针编译为只保存了函数的内存起始地址，因此可以在同一个单继承类中把一种类型的成员函数指针强制转换为另一种成员函数指针，或者把单继承派生类的成员函数指针强制转换为基类成员函数指针。这是打破了C++标准的违例办法。例如，对于CWnd::OnCommand函数，转换过程是：
<syntaxhighlight lang="cpp">BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)()</syntaxhighlight>
CString.
CString是MFC中最常见的类之一，用于封装字符串数据结构。它只有一个数据成员m_pszData，其值为字符串的首地址，其数据类型为wchar_t*或char*。在CString的m_pszData的前面实际还分配了CSringData数据块，包含了管理数据:
<syntaxhighlight lang="cpp">
IAtlStringMgr* pStringMgr; 
int nDataLength; 
int nAllocLength; 
int nRefs;
</syntaxhighlight>
自底向上，CAtlStringMgr提供内存管理，CStringData提供共享管理，CString提供字符串操作。
CAtlStringMgr的一个成员是IAtlMemMgr接口，这是策略模式，可以引用某个内存管理类。CAtlStringMgr的另一个成员是CNilStringData。
因此，每次为CString动态分配地址空间，实际分配长度为:codice_1。通过 Attach 操作，将这个 CStringData* 与 CSimpleStringT::m_pszData 执行了关联。当执行CString的默认构造函数生成一个空串时，实际上都是构造一个CnilStringData对象。CNilStringData 派生自 CStringData，额外拥有一个 achNil 的数组成员，这个数组初始化为空字符串。通过这个 achNil，保证了一个经过调用默认构造函数初始化的 CString，其指向的真正的字符串是一个空串。 
部分编译器对std::string放弃了写时复制（Copy On Write）机制。但是，CString一直采取这一机制。CSimpleStringT::Fork 函数就提供了这样一个操作，具体分为下面几步：
GetString方法返回的是只读的字符串地址；而GetBuffer方法返回的是可写的字符串地址（如果数据区是共享的，则写时复制），如果修改了字符串内容，这时需要调用ReleaseBuffer方法把新的字符串长度修改到元数据中（并在尾部增加2个0字节）。
CString对象用作可变参数函数（如printf）的实参时，由于无法通过形参类型确定调用哪个CString的类型转换操作符函数，因此有必要显式指明要转换的类型。如果需要在函数的参数传递CString，由于CString使用了引用计数，因此函数参数传递一个CString对象是可行的；不需要修改其内容时，推荐使用const CString&。
支持MFC的DLL开发.
使用Visual C++可以开发3种DLL:
由于DLL与调用它的应用程序都可以有自己的MFC全局数据与句柄映射（handle mapping），如果句柄值相同，则默认使用应用程序的映射到的资源。为了不互相干扰，允许DLL内部使用自己的资源，必须在DLL函数的入口处把资源模块句柄从默认的应用程序切换为该DLL。办法是：