Windows 程式設計/Microsoft Foundation Classes
本質上,MFC 是一個 SDK 介面,一個由一系列類組成的庫,這些類充當 Windows API 部分的包裝器,以便 C++ 程式設計師可以使用面向物件程式設計 (OOP) 正規化和 C++ 語言來編寫 Windows 程式(Win32 API 基於 C,如本書的 C 和 Win32 API 部分 所示)。應該學習 Win32 API 或至少了解一些概念,因為 MFC 中缺少一些函式,這將有助於更好地理解 SDK。
一些工具,例如 Microsoft Visual Studio,能夠自動生成大量用於專案中的 MFC 骨架程式碼。因此,大多數 MFC 教程或參考材料都將使用自動化的 Visual Studio 工具來教授該主題,並省略一些細枝末節。在這本書中,我們儘可能保持中立。
MFC 最初主要面向企業級程式設計專案,創建於大多數程式碼都使用 C 語言且面向物件程式設計僅限於 Smalltalk 領域的時代。
自 Visual Studio 6.0 和 MFC 6.0 釋出以來,由於該公司更青睞 .NET Framework,因此人們對 MFC 的未來支援知之甚少。版本 7.0、7.1 和 8.0 主要是在擴充套件以支援新的作業系統並幫助開發人員遷移到新的框架。從那時起,有關 MFC 未來資訊只能從 Steve Teixeira(微軟公司,2005 年 6 月)的論文中提取 - MFC:Visual Studio 2005 及以後,在 Visual Studio 2008 Service Pack 1 釋出時,微軟似乎再次積極支援 MFC。
如今,許多使用者發現,對於低複雜度的程式,30-80Mb 的記憶體佔用量是可以接受的(這在 Java 或 .Net 應用程式中很常見),響應時間較慢或諸如網際網路現在提供的“超出您的控制範圍”的應用程式也是如此。因此,在小型應用程式中使用 MFC 的影響是否大於庫提供的優勢尚有爭議。如今,大多數專門為 Windows 製作的軟體都使用 MFC。
如果您不打算
- 使用複雜的 GUI、使用文件/檢視架構或複雜的控制元件。
這將增加系統資源的使用(記憶體使用、exe 和安裝大小)。 - 使用依賴於 MFC 的其他庫。
- 為您的應用程式提供複雜的安裝。
則應首選 Win32 API SDK 或其替代包裝器。要分發基於 MFC 的專案,必須使用靜態 MFC 庫構建它或將專案與所需的 MFC dll 一起分發。不能保證您的客戶擁有程式所需的 dll。舊的基於 MFC 的程式必須與新的 MFC 版本一起工作。它們應該可以,但並不總是這樣。如果在安裝您的專案後,客戶的一些舊軟體產品開始掛起,他們將不會感到高興。
MFC 設計原則旨在簡化。包裝器類旨在簡化某些任務並自動化其他任務。但是,由於這些事實,從原始 Win32 API 中丟失了一定程度的微調控制或存檔了過度的自動化。MFC 被認為存在嚴重的架構缺陷和不一致性,並且沒有得到積極維護。C++ 語言和最佳實踐已經發展,如今這構成了使用該框架的障礙。
由於 MFC 早於 STL 標準化到 C++ 語言,因此它實現了 STL 容器的自身版本,這些版本並不完整甚至不一致,MFC 實現的這些簡單解決方案往往更快,但是您應該儘可能使用 STL,它將使程式碼更符合 C++ 標準並允許在將程式碼轉換為多平臺時更容易移植。
- 多重繼承
MFC 類庫不使用多重繼承,並且不是為完全支援多重繼承而設計的。
由於大多數 MFC 類都派生自 CObject,因此使用多重繼承會導致歧義問題(對 CObject 成員函式的任何引用都必須消除歧義)。靜態成員函式,包括operator new和operator delete也必須消除歧義。
最佳選擇是避免在 MFC 中使用多重繼承,但請檢視 在 MFC 中使用 C++ 多重繼承(msdn.microsoft.com)以獲取有關如何繞過這些限制所需的資訊。
MFC 使用 匈牙利命名法。它使用字首,例如“m_”表示成員變數或“p”表示指標,其餘名稱通常以駝峰命名法編寫(每個單詞的首字母大寫)。
- CObject 作為大多數 MFC 類的根
MFC 中所有重要的類都派生自 CObject 類。CObject 沒有任何成員資料,但具有一些預設功能。
MFC 需要與標準 <windows.h> 標頭檔案分開的標頭檔案。MFC 系統的核心需要包含 <afxwin.h>。其他一些有用的標頭檔案是 <afxext.h>(用於 MFC 擴充套件)和 <afxcmn.h>(用於 MFC 通用對話方塊)。
不幸的是,僅僅更改標頭檔案還不夠。MFC DLL 庫必須由專案連結,因為 DLL 檔案包含將在每個程式中使用的類定義。要使用 MFC,必須連結到 MFC 庫
stdafx.h 是 MFC 專案的標準包含檔案——也就是說,如果您建立一個新的 MFC 專案,則會自動為您建立一個 stdafx.h。它將包含所有其他必要的 MFC 標頭檔案。
extern CYourAppClass theApp;
在應用程式類的標頭檔案中使用,然後在需要使用 theApp 的任何地方包含它。
您可以嘗試使用AfxGetApp函式獲取指向theApp的指標,這是一種訪問應用程式成員的有效方法,即將指向theApp的指標作為需要它的類的成員變數——例如
class CMyDialog : public CDialog
{
// other class stuff here...
// Attributes
public:
CMdiApp* m_pApp;
};
並確保在建構函式中初始化m_pApp,否則將訪問NULL指標。
CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/): CDialog(CMyDialog::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDialog)
//}} AFX_DATA_INIT
// Outside the special-format comments above...
m_pApp = (CMdiApp*)AfxGetApp( );
}
瞧!現在任何時候您都需要訪問您的應用程式,您都可以獲得它!
m_pApp->m_nMemberVar;
m_pApp->MemberFunction(nParam1, strParam2);
首先必須提到,MFC 不是那種“看起來像 C”的 C++ 程式設計風格。MFC 大量使用了 C++ 的面向物件特性,對於新的 C++ 程式設計師來說,這可能看起來“密集”甚至難以閱讀。強烈建議讀者現在熟悉 C++ 概念,例如類和層次結構,如果他們還不熟悉這些概念。
MFC 的根類是CObject類。CObject 本身不支援多重繼承,但派生類支援。每個應用程式都從CWinApp派生的類開始。每個程式都必須有一個 CWinApp 類,並且每個應用程式只能有一個。CWinApp 包含許多用於初始化應用程式和控制例項控制代碼(類似於 WinMain 函式的 HINSTANCE 成員)的函式。想要顯示視窗的程式必須使用CWnd類的派生類。
我們將在此處概述一個基本的 MFC 程式,該程式將建立一個簡單的視窗,但不會處理任何使用者輸入。從這個基本概述開始,我們將能夠解決更困難的問題。
#include <afxwin.h> //basic MFC include
//class derived from CFrameWnd, which is derived from CWnd
class Basic_Window:public CFrameWnd
{
public:
Basic_Window()
{
Create(NULL, "Basic MFC Window");
// In some cases you might want to use
// Create(NULL, _T(":Basic MFC Window"));
}
};
//class derived from CWinApp, which is the main instance of our application
class MyProgram:public CWinApp
{
//a pointer to our window class object
Basic_Window *bwnd;
public:
//this is essentially our "entry point"
BOOL InitInstance()
{
bwnd = new Basic_Window();
m_pMainWnd = bwnd;
m_pMainWnd->ShowWindow(1);
return 1;
}
};
//the program class instance pointer
MyProgram theApp;
正如我們在這裡看到的,我們立即依賴於類定義和繼承,因此對於不完全熟悉這些主題的讀者來說,在繼續之前最好先複習一下。
MFC 提供了許多全域性變數,這些變數被例項化,然後在底層的 MFC 框架中使用以編譯您的程式。當我們使用變數m_pMainWnd時,可以在我們的基本示例程式碼中看到這一點。
通用解決方案是PostQuitMessage([退出程式碼]);,但請注意清理任何殘留的資源(關閉文件、釋放記憶體和資源、銷燬建立的任何其他視窗等),另一方面,使用AfxGetMainWnd()->PostMessage( WM_CLOSE );在某些情況下可能是一種更好的方法,因為它會觸發正確的關閉順序。這在 MDI/SDI 應用程式中尤其重要,因為它讓文件有機會在退出前提示儲存或讓使用者取消退出。
為了顯示繁忙/等待滑鼠游標,MFC 添加了一個簡單的輔助類。在函式內部例項化一個 CWaitCursor 類,然後在該函式的持續時間內顯示等待游標,類的自動銷燬將恢復游標狀態。
CWaitCursor aWaitCursor;
正如預期的那樣,MFC 也將其自身的特殊類和函式包裝在 Win32 執行緒原語中。有關執行緒和 C++ 的一般資訊,請參見C++ 程式設計 Wikibook 關於多工處理的部分。MFC 將執行緒設計為工作執行緒和 GUI 執行緒。
工作執行緒特別適用於執行後臺任務或任何不需要使用者干預的非同步工作,例如列印作業、計算、等待事件等。要建立工作執行緒,最簡單的方法是實現一個執行所需工作的函式,然後使用AfxBeginThread()建立執行緒,該執行緒將使用該特定函式。
除非您別無選擇以保證執行緒終止,否則永遠不要使用TerminateThread()。這是不好的做法並且很危險。
可以透過從執行緒中正常返回(完成)或發出訊號使其過早返回來實現正確的執行緒退出。由於我們在 Windows 上並且在 32 位繫結 CPU 上(使 32 位訪問成為原子操作),因此使用共享bool變數來指示執行緒退出被認為是安全的(但不可移植),如果可用,可以使用任何其他同步方法。
由於關於執行緒終止的唯一問題是在中止作業或退出程式時沒有執行的執行緒,因此在處理工作執行緒時,在建立執行緒的類的解構函式中,您應該向執行緒發出中止訊號,然後使用WaitForSingleObject()等待執行緒終止。
