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的。

## 版本

產品｜程式庫｜版本｜發佈日期

名稱｜版本

Microsoft C/C++ 7.0｜mafxcw.lib｜MFC 1.0｜1992

Microsoft Visual C++ 1.0｜Visual C++ 1.0｜mfc200.dll｜MFC 2.0

Visual C++ 1.5｜mfc250.dll｜MFC 2.5

Visual C++ 1.51｜mfc250.dll｜MFC 2.51

Visual C++ 1.52c｜mfc250.dll｜MFC 2.5 ( Windows 3.x 的最後發展平台 )

Microsoft Visual C++ 2.0｜Visual C++ 2.0｜mfc30.dll｜MFC 3.0

Visual C++ 2.1｜mfc30.dll｜MFC 3.1

Visual C++ 2.2｜mfc30.dll｜MFC 3.2

Microsoft Visual C++ 4.0｜Visual C++ 4.0｜mfc40.dll｜MFC 4.0 (mfc40.dll 包含於 Windows 95)｜1995年8月

Visual C++ 4.1｜mfc40.dll｜MFC 4.1

Visual C++ 4.2｜mfc42.dll｜MFC 4.2 (mfc42.dll 包含於 Windows 98 初版)｜1998年3月

內含於 Visual C++ 3.0｜mfc42.dll｜MFC 4.2

Microsoft Visual C++ 5.0｜Visual C++ 5.0｜mfc42.dll｜MFC 4.21, 是 MFC 4.2 的主要更新

Microsoft Visual C++ 6.0｜Visual C++ 6.0｜mfc42.dll｜MFC 6.0｜1998年

內含於 Visual C++ 4.0｜mfcce400.dll｜MFC 6.0

Microsoft Visual C++ .NET 2002｜Visual C++ .NET 2002 (Visual C++ 7.0)｜mfc70.dll｜MFC 7.0｜2002年2月13日

Microsoft Visual C++ .NET 2003｜Visual C++ .NET 2003 (Visual C++ 7.1)｜mfc71.dll｜MFC 7.1｜2003年4月24日

Visual C++ .NET 2003 + MS11-025 (KB2465373)｜MFC 7.10.6119.0｜2011年4月12日

Microsoft Visual C++ 2005｜Visual C++ 2005 (Visual C++ 8.0)｜mfc80.dll｜8.0.50727.42｜2006年4月10日

Visual C++ 2005 SP1｜8.0.50727.762｜2007年8月27日

Visual C++ 2005 SP1 + MS09-035 (KB973544)｜8.0.50727.4053｜2009年7月28日

Visual C++ 2005 SP1 + MS11-025 (KB2467175)｜8.0.50727.5592｜2011年4月12日

Visual C++ 2005 SP1 + MS11-025 (KB2538242)｜8.0.50727.6195 (包含於 redist 8.0.61000/8.0.61001)｜2011年6月14日

Microsoft Visual C++ 2008｜Visual C++ 2008 (Visual C++ 9.0)｜mfc90.dll｜9.0.21022.8｜2007年11月19日

Visual C++ 2008 + MS09-035 (KB973551)｜9.0.21022.218｜2009年7月28日

Visual C++ 2008 含Feature Pack｜9.0.30411｜2008年4月22日

Visual C++ 2008 SP1｜9.0.30729.1｜2008年8月11日

Visual C++ 2008 SP1 + MS09-035 (KB973552)｜9.0.30729.4148｜2009年7月28日

Visual C++ 2008 SP1 + MS11-025 (KB2467174)｜9.0.30729.5570｜2011年4月12日

Visual C++ 2008 SP1 + MS11-025 (KB2538243)｜9.0.30729.6161 (安裝程式版本 9.0.30729.5677)｜2011年6月14日

Microsoft Visual C++ 2010｜Visual C++ 2010 (Visual C++ 10.0)｜mfc100.dll｜10.0.30319.1｜2010年4月12日

Visual C++ 2010 + MS11-025 (KB2467173)｜10.0.30319.415｜2011年4月12日

Visual C++ 2010 SP1｜10.0.40219.1｜2011年3月10日

Visual C++ 2010 SP1 + MS11-025 (KB2565063)｜10.0.40219.325｜2011年8月9日

Microsoft Visual C++ 2012｜Visual C++ 2012 (Visual C++ 11.0)｜mfc110.dll｜11.0.50727.1｜2012年9月12日

Visual C++ 2012 Update 1｜11.0.51106.1｜2012年11月26日

Visual C++ 2012 Update 3｜11.0.60610.1｜2013年6月26日

Visual C++ 2012 Update 4｜11.0.61030.0｜2013年11月13日

Microsoft Visual C++ 2013｜Visual C++ 2013 (Visual C++ 12.0)｜mfc120.dll｜12.0.21005.1｜2013年10月17日

Visual C++ 2013 Update 2｜12.0.30501.0｜2014年5月12日

Visual C++ 2013 Update 5 + KB3138367｜12.0.40649.5｜2016年2月?

Visual C++ 2013 Update 5 + KB3179560｜12.0.40660.0｜2016年7月8日

Visual C++ 2013 Update 5 + KB4032938｜12.0.40664.0｜2017年7月18日

Microsoft Visual C++ 2015｜Visual C++ 2015 (Visual C++ 14.0)｜mfc140.dll｜14.0.23026.0｜2015年7月20日

Visual C++ 2015 Update 1｜14.0.23506.0｜2015年11月30日

Visual C++ 2015 Update 2｜14.0.23918.0｜2016年3月30日

Visual C++ 2015 Update 3｜14.0.24210.0｜2016年6月27日

Visual C++ 2015 Update 3｜14.0.24212.0｜2016年8月18日

Visual C++ 2015 Update 3 + KB3165756｜14.0.24215.1｜2016年9月15日

Microsoft Visual C++ 2017｜Visual C++ 15.0｜mfc140.dll｜14.10.25008.0｜2017年3月7日

Visual C++ 15.1?｜14.10.25017.0｜2017年4月5日?

Visual C++ 15.2｜14.11.25325.0｜2017年5月10日

Visual C++ 15.4｜14.12.25810.0｜2017年10月9日

Visual C++ 15.6｜14.13.26020.0｜2018年3月5日

Visual C++ 15.7｜14.14.26405.0｜2018年5月7日

Visual C++ 15.7.3?｜14.14.26429.4｜2018年5月31日

Visual C++ 15.8｜14.15.26706.0｜2018年8月14日

Visual C++ 15.9｜14.16.27012.6｜2018年11月13日

Visual C++ 15.9.5｜14.16.27024.1｜2019年1月8日

Visual C++ 15.9.6｜14.16.27026.1｜2019年1月24日

Visual C++ 15.9.9｜14.16.27027.1｜2019年3月12日

Visual C++ 15.9.15｜14.16.27033.0｜2019年8月13日

Microsoft Visual C++ 2019｜Visual C++ 16.0.0｜mfc140.dll｜14.20.27508.1｜2019年4月2日?

Visual C++ 16.1.0｜14.21.27702.2｜2019年5月21日

Visual C++ 16.2.0｜14.22.27821.0｜2019年7月24日

Visual C++ 16.3.0｜14.23.27820.0｜2019年9月23日

Visual C++ 16.4.0｜14.24.28127.4｜2019年12月3日

Visual C++ 16.5.0｜14.25.28508.3｜2020年3月16日

Visual C++ 16.6.0｜14.26.28720.3｜2020年5月19日

Visual C++ 16.6.3｜14.26.28808.1｜2020年7月30日

Visual C++ 16.7.0｜14.27.29016.0｜2020年8月5日

Visual C++ 16.7.5｜14.27.29112.0｜2020年9月29日

Visual C++ 16.8.0｜14.28.29325.2｜2020年11月10日

Visual C++ 16.8.2｜14.28.29334.0｜2020年11月19日

Visual C++ 16.9.0｜14.28.29910.0｜2021年3月2日

Visual C++ 16.9.2｜14.28.29913.0｜2021年3月16日

Visual C++ 16.9.4｜14.28.29914.0｜2021年4月13日

Visual C++ 16.10.0｜14.29.30037.0｜2021年5月25日

Visual C++ 16.10.2｜14.29.30038.0｜2021年6月15日

Visual C++ 16.10.4｜14.29.30040.0｜2021年7月20日

Visual C++ 16.11.0｜14.29.30133.0｜2021年8月10日

Visual C++ 16.11.4｜14.29.30135.0｜2021年10月5日

Microsoft Visual C++ 2022｜Visual C++ 17.0.0RC｜mfc140.dll｜14.30.30704.0｜2021年10月13日

- 1 Visual Studio速成版（Express）不包含MFC程式庫。
- 2 Feature Pack只用於英文版本的Visual Studio 2008。非英文版本的支援於將包含於Visual Studio 2008 Service Pack 1。

## MFC的結構

作為一個應用程式的開發框架，必須滿足各方面的功能需求。

### 應用程式啟動

基於MFC開發的應用程式在啟動時，Windows作業系統：

- 首先調用WinMain函數（位於appmodul.cpp中，封裝到mfc80.dll（VS2005版）），WinMain函數內調用了AfxWinMain函數。
- AfxWinMain函數（位於WinMain.cpp中）調用了
- 該應用程式自定義的App類（這個類派生於CWinApp的，CWinApp又是派生於CWinThread，因此代表了應用程式的主線程）的InitInstance函數，該函數註冊並創建窗口（通過AppUI2.cpp中的ProcessShellCommmand函數)，然後ShowWindow、UpdateWindow;
- CWinThread的InitInstance函數。
- CWinThread的Run函數（位於thrdcore.cpp中）。該函數內部是Windows的消息循環。當應用程式收到WM_QUIT消息後，CWinThread::Run函數返回，緊接著CWinThread::ExitInstance被調用，該函數可被覆蓋。程序至此退出運行。消息循環是一個for的死循環，該死循環內部包含了一個do...while的循環結構。while循環條件是調用PeekMessage函數的返回值，如果當前UI線程消息隊列為空就返回到外層的死循環；while循環體內做兩件事：
- PumpMessage。實際調用AfxInternalPumpMessage函數實現其功能：GetMessage、AfxPreTranslateMessage、TranslateMessage、DispatchMessage.即：從UI線程消息隊列移除一條消息、遍歷該消息的CWnd類直到該窗口的各級別父窗口的CWnd類以提供預處理該消息的機會、如果該消息是按鍵消息則翻譯為WM_CHAR消息、把該消息給相應的窗口函數。
- IsIdleMessage：實際調用了AfxInternalIsIdleMessage函數，對於WM_PAINT、WM_SYSTIMER、以及光標位置沒有變化的WM_MOUSEMOVE或WM_NCMOUSEMOVE，為Idle Message。
- 各個窗口函數（WndProc）內部首先獲取對應當前窗口句柄的CWnd類的指針，然後調用AfxCallWndProc函數。

### 應用程式結束

- 如果是點擊了IDOK按鈕，默認是調用OnOK，然後是OnDestory，最後是PostNcDestroy
- 如果點擊IDCANCEL按鈕，默認調用OnCancel，然後是OnDestory，最後是PostNcDestroy
- 如果點擊右上角的關閉按鈕：先OnClose，然後是OnCancel，再然後是OnDestory，最後是PostNcDestroy

### 消息循環與消息映射

<image>

#### 消息分類

分類｜消息｜映射宏｜消息處理函數原型｜注釋

系統消息｜標準Windows消息｜MFC使用專用的相關的宏。如ON_WM_CREATE｜MFC使用專用的消息處理成員函數｜一般地由窗口對象來處理這類消息

命令消息 WM_COMMAND｜ON_COMMAND(id, memberFxn)｜void OnXXX ｜通過標識符ID來區分來自哪個菜單項、工具欄按鈕或者加速鍵等

ON_COMMAND_RANGE(id, idLast, memberFxn)｜void OnXXX (UINT id)｜批量處理一定範圍內的標示符ID

ON_UPDATE_COMMAND_UI(id, idLast, memberFxn)｜void OxXXX(CCmdUI、pCmdUI)｜程序空閒時發的界面更新消息的宏

ON_UPDATE_COMMAND_UI_RANGE(id, idLast, memberFxn)｜void OxXXX(CCmdUI、pCmdUI)

ON_CONTROL(id, idLast, memberFxn)｜void OnXXX ｜父窗口響應控制項發送的消息

ON_CONTROL_RANGE(id, idLast, memberFxn)｜void OnXXX (UINT id)

ON_CONTROL_REFLECT(id, idLast, memberFxn)｜void OnXXX ｜子控制項響應父窗口反射回來的通知消息

通知消息WM_NOTIFY｜ON_NOTIFY(wNotifyCode,id,memberFxn)｜afx_msg void memberFxn(NMHDR、pNotifyStruct, LRESULT、result)｜wParam為控制項ID，lParam指向NMHDR結構體，結構體的code域值為控制項通知碼用來表示控制項上的動作，如NM_CLICK.一般地由父窗口對象來處理這類消息。

ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )｜可手工添加（class wizard不支持）,處理控制項id連續的一批控制項的同一通知碼消息。

ON_NOTIFY_EX｜允許通知消息在多處被處理

ON_NOTIFY_EX_RANGE

ON_NOTIFY_REFLECT｜afx_msg void memberFxn(NMHDR、pNotifyStruct, LRESULT、result)｜複雜子控制項響應父窗口反射回來的通知消息

自定義消息｜窗口類內部自定義消息WM_USER到WM_APP-1｜ON_MESSAGE(消息名,memberFxn)｜afx_msg LRESULT OnMyMessageXXX(WPARAM wParam, LPARAM lParam)｜示例

程序內部自定義消息WM_APP到0xBFFF｜ON_THREAD_MESSAGE(WM_THREADMSG,OnThreadMessage)｜afx_msg void OnThreadMessage(WPARAM wParam,LPARAM lParam);｜示例

ON_REGISTERED_THREAD_MESSAGE｜afx_msg void OnMyRegisterdThreadMsg(WPARAM, LPARAM);｜RegisterWindowMessage使用一個字串來登記一個自定義的消息ID

應用程式之間自定義消息0xC000到0xFFFF｜ON_REGISTERED_MESSAGE｜LRESULT OnMyMessageXXX(WPARAM wParam, LPARAM lParam)｜RegisterWindowMessage使用一個字串來登記一個自定義的消息ID，便於跨進程應用

#### 控制項的通知消息與反射消息

窗體上的控制項，應當向父窗體通報控制項發生的各種事件，如被點擊、繪製、內容改變等等，稱為通知消息（notification message）。在 Windows 3.x的16位程序設計時代，控制項向父窗體發送WM_COMMAND消息，由父窗體的代碼負責實現這些事件。其中wParam的低16位是 control ID，高16位是notification code (例如BN_CLICKED)；lParam是控制項句柄。因此，再無可能傳遞其它資訊給父窗體。為此，為傳遞具有特別內容的控制項事件，Windows 3.x定義了一批特殊的通知消息(notification messages)：

- WM_PARENTNOTIFY:子窗口的某些重大事件發生時通知父窗口，包括創建、銷毀、滑鼠各鍵按下等事件
- 子窗口的滾動情況的通知消息
- WM_VSCROLL
- WM_HSCROLL
- 子窗口的繪製通知消息
- WM_DRAWITEM,
- WM_MEASUREITEM,
- WM_COMPAREITEM,
- WM_DELETEITEM,
- WM_CHARTOITEM,
- WM_VKEYTOITEM
- WM_CTLCOLOR：設置按鈕、編輯框、ListBox、Static、滾動條控制項與MessageBox、DialogBox的前景色、背景色、背景模式、字體，並返回一把Brush，用於控制項背景繪製。

早於4.0版本的MFC，在控制項類提供了虛函數處理這些通知消息，這一辦法已經被下述的「消息反射」取代（但仍然向後兼容繼續支持）。

隨著Windows 95開始了32位程序時代，伴之而來的是Win32 API 與 MFC 4.0。Win32增加了很多複雜的控制項，需要使用更多的通知消息傳遞很多複雜的數據給父窗體。Win32 API僅僅增加了一個消息WM_NOTIFY，就實現了這些功能。lParam參數開頭是NMHDR數據結構，其後是與該通知類型相關的特定數據結構。

```cpp
typedef struct tagNMHDR {
    HWND hwndFrom;
    UINT idFrom;
    UINT code;
} NMHDR;
```

CWnd::OnNotify函數處理通知消息。它的默認實現是檢查消息映射表（message map）查找通知的處理器函數並調用。一般說來，不必覆蓋OnNotify；而應該寫一個處理器函數並增加為該窗口類的消息映射表條目。

```
 ON_NOTIFY(wNotifyCode, id, memberFxn)
```

成員函數應該寫為：

```
 afx_msg void memberFxn(NMHDR* pNotifyStruct, LRESULT* result);
```

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。但以下三種情況，反射消息處理函數可以隨意自行起名，對應的消息反射宏的名字分別為：

- WM_COMMAND使用ON_CONTROL_REFLECT
- WM_NOTIFY使用ON_NOTIFY_REFLECT
- ON_UPDATE_COMMAND_UI使用ON_UPDATE_COMMAND_UI_REFLECT

#### 消息傳遞處理機制

MFC類體系中，Windows消息傳遞處理機制是基於CCmdTarget類及其派生類的靜態成員函數GetThisMessageMap內部定義的靜態數據成員：

- 成員類型為AFX_MSGMAP_ENTRY的陣列_messageEntries。在類的實現文件中，在BEGIN_MESSAGE_MAP與END_MESSAGE_MAP之間的內容來初始化消息映射入口項陣列。
- 數據類型為AFX_MSGMAP的變量messageMap。該結構包含兩項，分別是直接基類GetThisMessageMap函數指針與本類的_messageEntries陣列首元素地址。

在頭文件的類定義中使用宏DECLARE_MESSAGE_MAP來聲明靜態成員函數GetThisMessageMap與虛函數GetMessageMap

使用者所寫的類的Windows消息處理函數（例如OnCommand）必須轉換為CCmdTarget::*的成員函數指針類型AFX_PMSG，保存在該類的_messageEntries陣列中。

```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);
```

調用使用者類中該消息處理函數時，根據該函數保存在_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函數，轉換過程是：

```cpp
BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)()
```

## CString

CString是MFC中最常見的類之一，用於封裝字串數據結構。它只有一個數據成員m_pszData，其值為字串的首地址，其數據類型為wchar_t*或char*。在CString的m_pszData的前面實際還分配了CSringData數據塊，包含了管理數據:

```cpp
IAtlStringMgr* pStringMgr; 
int nDataLength; 
int nAllocLength; 
int nRefs;
```

自底向上，CAtlStringMgr提供記憶體管理，CStringData提供共享管理，CString提供字串操作。

CAtlStringMgr的一個成員是IAtlMemMgr接口，這是策略模式，可以引用某個記憶體管理類。CAtlStringMgr的另一個成員是CNilStringData。

因此，每次為CString動態分配地址空間，實際分配長度為:`(nChars+1)*nCharSize+sizeof(CStringData)`。通過 Attach 操作，將這個 CStringData* 與 CSimpleStringT::m_pszData 執行了關聯。當執行CString的默認構造函數生成一個空串時，實際上都是構造一個CnilStringData對象。CNilStringData 派生自 CStringData，額外擁有一個 achNil 的陣列成員，這個陣列初始化為空字符串。通過這個 achNil，保證了一個經過調用默認構造函數初始化的 CString，其指向的真正的字串是一個空串。

部分編譯器對std::string放棄了寫時複製（Copy On Write）機制。但是，CString一直採取這一機制。CSimpleStringT::Fork 函數就提供了這樣一個操作，具體分為下面幾步：

- 根據傳入的一個長度分配一段新的空間；—— Allocate(nLength, ...)
- 把舊數據拷貝到新的空間裡面；—— CopyChars(...)
- 舊數據塊的引用技術減1； —— pOldData->Release
- 把 m_pszData 和新的數據塊關聯起來。—— Attach(pNewData)

GetString方法返回的是只讀的字串地址；而GetBuffer方法返回的是可寫的字串地址（如果數據區是共享的，則寫時複製），如果修改了字串內容，這時需要調用ReleaseBuffer方法把新的字串長度修改到元數據中（並在尾部增加2個0位元組）。

CString對象用作可變參數函數（如printf）的實參時，由於無法通過形參類型確定調用哪個CString的類型轉換操作符函數，因此有必要顯式指明要轉換的類型。如果需要在函數的參數傳遞CString，由於CString使用了引用計數，因此函數參數傳遞一個CString對象是可行的；不需要修改其內容時，推薦使用const CString&。

## 支持MFC的DLL開發

使用Visual C++可以開發3種DLL:

- 不使用MFC的DLL。
- 使用MFC的規則的DLL：輸出的函數不涉及MFC，因此可以被支持/不支持MFC的應用程式調用該DLL
- 動態連結到MFC（Regular DLLs dynamically linked to MFC）。
- 靜態連結到MFC（Regular DLLs statically linked to MFC）
- 使用MFC的擴展DLL（Extension DLLs），只能動態連結到MFC：輸出的函數涉及MFC，也可以輸出基於MFC的派生類。

由於DLL與調用它的應用程式都可以有自己的MFC全局數據與句柄映射（handle mapping），如果句柄值相同，則默認使用應用程式的映射到的資源。為了不互相干擾，允許DLL內部使用自己的資源，必須在DLL函數的入口處把資源模塊句柄從默認的應用程式切換為該DLL。辦法是：

- 在該DLL的每個輸出的函數的最開始之處調用`AFX_MANAGE_STATE(AfxGetStaticModuleState( ))`。函數AfxGetStaticModuleState的功能是在運行棧上創建一個AFX_MODULE_STATE類的實例，對其進行設置，函數返回值為AFX_MODULE_STATE的指針。AFX_MODULE_STATE類利用其構造函數和析構函數進行模塊狀態現場存儲及恢復。
- 使用AfxGetResourceHandle;獲取當前資源模塊句柄。使用AfxSetResourceHandle(HINSTANCE xxx); 設置程序要使用的資源模塊句柄。

## 常用頭文件與庫文件

- STDAFX.H 載入其他MFC頭文件。
- AFXWIN.H 它和它載入的文件聲明了所有的MFC類。其內包含AFX.H，後者包含了AFXVER_.H，後者又載入了AFXV_W32.H，後者又載入WINDOWS.H。
- AFXEXT.H 使用工具欄、狀態欄的程序必須載入這個文件
- AFXDLGS.H 通用對話框（Common dialog）的MFC程序需要載入此文件。它內部包含COMMDLG.H
- FXCMN.H 通用控制項（common control）的MFC程序需要載入此文件。
- AFXCOLL.H 使用MFC提供的容器都需要載入此文件。
- AFXDLLX.H 凡使用MFC extension DLLs需要載入此文件。
- AFXRES.H MFC程序的RC文件必須載入此文件。此文件中對於標準資源的ID都有默認值。它們定義於此文件中。

## 相關條目

- 活動模板庫（Active Template Library，ATL）
- Microsoft Visual Studio
- Visual Studio .NET
- Object Windows Library