Visual Studio .NET(2002/2003)帶來了全中文化MSDN、強化的類別檢視與智慧提示,大幅降低MFC學習門檻。本文將以VS.NET建立的對話框專案為例,逐行剖析AppWizard生成的樣板代碼結構,並以「檔案複製工具」為實戰案例,示範控制項變數綁定、常用對話框(檔案選取、訊息框)、WinAPI封裝類別(CFileDialog、CString)的實戰用法。
| 步驟 | VC++ 6.0 | VS.NET (2002/2003) |
|---|---|---|
| 新建專案 | File→New→Projects→MFC AppWizard(exe) | File→New→Project→Visual C++ Projects→MFC Application |
| 專案類型 | Step 1 選 Dialog based | Application Type 選 Dialog based |
| 完成 | Finish | Finish |
類別檢視中可見三個類別:
CApp (如 CDialogApp)——應用程式類別,繼承 CWinAppCDlg (如 CDialogDlg)——主對話框類別,繼承 CDialogCAboutDlg——關於對話框,繼承 CDialogclass CDialogApp : public CWinApp {
public:
virtual BOOL InitInstance(); // 唯一需覆寫的初始化函數
};
WinMain,內部建立唯一 CWinApp 派生物件 theApptheApp 為全域物件,最早構造,可透過 AfxGetApp() 取得指標存取資源WinMain → InitApplication() → InitInstance() → Run()(訊息迴圈) → ExitInstance()BOOL CDialogApp::InitInstance()
{
InitCommonControls(); // XP視覺樣式支援(ComCtl32 v6+)
CWinApp::InitInstance(); // 父類別預設初始化
AfxEnableControlContainer(); // ActiveX控制項容器支援
CDialogDlg dlg; // 建立模態對話框物件(棧上)
m_pMainWnd = &dlg; // 設為主視窗
INT_PTR nResponse = dlg.DoModal(); // 模態顯示,阻塞直到關閉
if (nResponse == IDOK) { /* 確定鍵關閉處理 */ }
else if (nResponse == IDCANCEL) { /* 取消鍵關閉處理 */ }
return FALSE; // 對話框關閉後直接退出應用程式(不進入Run迴圈)
}
關鍵點:DoModal() 內部跑自己的訊息迴圈,返回 IDOK/IDCANCEL;InitInstance 回傳 FALSE 讓框架知曉無需進入主訊息泵。
class CDialogDlg : public CDialog {
public:
CDialogDlg(CWnd* pParent = NULL); // 建構函數
enum { IDD = IDD_DIALOG_DIALOG }; // 對話框模板資源ID
protected:
virtual BOOL OnInitDialog(); // 對話框初始化
afx_msg void OnSysCommand(UINT, LPARAM); // 系統選單
afx_msg void OnPaint(); // 重繪
afx_msg HCURSOR OnQueryDragIcon(); // 最小化圖示
DECLARE_MESSAGE_MAP()
};
繼承鏈:CDialogDlg → CDialog → CWnd → CCmdTarget → CObject
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()
這是分析程式功能的最佳切入點——直接看支援哪些訊息、對應哪些處理函數。
BOOL CDialogDlg::OnInitDialog()
{
CDialog::OnInitDialog(); // 父類別初始化
// 將「關於...」加入系統選單
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu) {
CString strAbout;
strAbout.LoadString(IDS_ABOUTBOX); // 從字串表載入
if (!strAbout.IsEmpty()) {
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAbout);
}
}
SetIcon(m_hIcon, TRUE); // 大圖示
SetIcon(m_hIcon, FALSE); // 小圖示
return TRUE; // 未手動設定焦點時回傳TRUE
}
刪除預設控制項,新增:
IDC_SOURCE(來源)、IDC_TARGET(目標)IDC_BROWSE_SRC、IDC_BROWSE_DST、IDC_COPY、IDC_CANCEL
粗體字為控制項ID
使用類別精靈(Ctrl+W)為4按鈕加入 BN_CLICKED 處理函數:
OnBnClickedBrowseSrc()OnBnClickedBrowseDst()OnBnClickedCopy()OnBnClickedCancel()
void CFileCopyDlg::OnBnClickedCancel()
{
EndDialog(IDCANCEL); // 以IDCANCEL結束模態對話框
}
// 來源瀏覽:開啟舊檔
void CFileCopyDlg::OnBnClickedBrowseSrc()
{
CFileDialog dlg(TRUE, _T(""), _T(""),
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T("所有檔案 (*.*)|*.*||"), this);
if (dlg.DoModal() == IDOK) {
CString path = dlg.GetPathName();
SetDlgItemText(IDC_SOURCE, path);
}
}
// 目標瀏覽:另存新檔
void CFileCopyDlg::OnBnClickedBrowseDst()
{
CFileDialog dlg(FALSE, _T(""), _T(""),
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T("所有檔案 (*.*)|*.*||"), this);
if (dlg.DoModal() == IDOK) {
CString path = dlg.GetPathName();
SetDlgItemText(IDC_TARGET, path);
}
}
關鍵點:
TRUE=開啟舊檔 / FALSE=另存新檔「描述|*.ext|描述2|*.ext2||」 雙管號結尾GetPathName() 取得完整路徑,SetDlgItemText 設定控制項文字
void CFileCopyDlg::OnBnClickedCopy()
{
CString src, dst;
GetDlgItemText(IDC_SOURCE, src);
GetDlgItemText(IDC_TARGET, dst);
if (src.IsEmpty() || dst.IsEmpty()) {
MessageBox(_T("請先選擇來源與目標檔案!"), _T("提示"), MB_ICONWARNING);
return;
}
// CopyFile: 來源、目標、TRUE=目標存在時失敗 / FALSE=覆蓋
if (CopyFile(src, dst, FALSE)) {
MessageBox(_T("檔案複製成功!"), _T("完成"), MB_OK | MB_ICONINFORMATION);
} else {
DWORD err = GetLastError();
CString msg;
msg.Format(_T("複製失敗(錯誤碼:%lu)"), err);
MessageBox(msg, _T("錯誤"), MB_OK | MB_ICONERROR);
}
}
CString 可直接當字串用、支援 Format、運算子 + 串接,極大簡化字串操作。
| MFC類別 | 封裝對象 | 常用方法 |
|---|---|---|
CWnd | 視窗 (HWND) | CreateWindow, ShowWindow, SetWindowText, GetDlgItemText, MessageBox |
CDialog | 對話框 | DoModal, OnInitDialog, EndDialog, Get/SetDlgItemText |
CFileDialog | 檔案開啟/儲存對話框 | DoModal, GetPathName, GetFileName, GetFileExt |
CString | 字串 | Format, LoadString, IsEmpty, GetLength, 運算子+ |
CMenu | 選單 (HMENU) | GetSystemMenu, AppendMenu, LoadMenu |
InitInstance、DoModal、訊息對應表的運作邏輯下一篇將進入 動態連結庫(DLL)開發:__declspec(dllexport)、.def 檔、隱式/顯式連結、MFC擴展DLL與一般DLL差異、DLL記憶體管理陷阱。