MFC自動生成程式碼詳解(一)
首先宣告這篇部落格是給MFC剛剛上路的coder準備的,老鳥們就自覺無視我吧!
大家有沒有感覺,建立MFC工程時他總會生成一大堆檔案一大堆程式碼。雖然給我們帶來了便利,但是除錯的時候碰到這些程式碼總是畏首畏腳的,從來不敢動他們。這篇部落格就來幫大家解決這個問題。讓你在熟悉他們的作用同時,真正的不怕他們!
現在我見了一個工程名字為Example他會生成以上幾個檔案
我們就從從Example.cpp開始,先看最中間的
CExampleApp theApp; //application object
這段程式碼其實就是本程式的application object,每個程式有且僅有一個。當程式執行時,這個全域物件產生,構造是執行起來。CWinApp 之中的成員變數將因為theApp 這個全域物件的誕生而獲得配置與初值。此段執行完後winmain登場,由於winmain並不在這幾個檔案夾了,所以不能直觀的看到他。
int AFXAPI AfxWinMain (...) { CWinApp* pApp = AfxGetApp(); AfxWinInit(...); pApp->InitApplication(); pApp->InitInstance(); nReturnCode = pApp->Run(); AfxWinTerm(); }
theapp獲得初值後,上面的程式碼開始執行。至於上面幾個函式具體的程式碼實現會在後面的連載中一一介紹,在這裡先交待一下他們的作用。
AfxWinInit(...);
這句程式碼的意思借用《深入淺出MFC》的一句話,。MFC中的 AfxWinInit的確會為我們註冊四個視窗類別,但不再是在AfxWinInit 中完成。
pApp->InitApplication();
本段程式碼相當於CMyWinApp::InitApplication();CMyWinApp 繼承自CWinApp,而InitApplication 又是CWinApp 的一個虛擬函式;我們並沒有改寫它(大部份情況下不需改寫它),所以上述動作相當於呼叫:CWinApp::InitApplication();這些動作都是MFC 為了內部管理而做的。
這段程式碼和上面的程式碼一樣,也相當於呼叫CMyWinApp::InitInstance();但是看下面的Example.cpp中的InitInstance()被我們改寫了,所以說上述動作的的確確就是呼叫我們自己(CMyWinApp)的這個InitInstance 函式。我們將在該處展開我們的主視窗生命。pApp->InitInstance();
nReturnCode = pApp->Run();
此時程式會執行Example.cpp中的InitInstance(),所以上面的run()函式我們先跳過。
#include "stdafx.h" #include "Example.h" #include "ExampleDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CExampleApp BEGIN_MESSAGE_MAP(CExampleApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CExampleApp 構造 CExampleApp::CExampleApp() { // TODO: 在此處新增構造程式碼, // 將所有重要的初始化放置在 InitInstance 中 } // 唯一的一個 CExampleApp 物件 CExampleApp theApp; // CExampleApp 初始化 BOOL CExampleApp::InitInstance() {
程式剛好執行的上面一句我們很多初始化都寫在這個函式裡,包括建立類物件,m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow();兩個函式這樣再加上建構函式中的Create()函式,我的的視窗就這樣生成了。很簡單吧!
nReturnCode = pApp->Run();
下面就到這段程式碼的了。MFC的訊息機制也從這開始了。在上面視窗建立完之後訊息佇列中出現了一個WM_PAINT 訊息,等待被處理。現在,執行的腳步到達pApp->Run。當然Run()和上面一樣相當於CMyWinApp::Run();而Run 又是CWinApp 的一個虛擬函式。所以說他和initapplicationa()一樣。執行的這裡下面就是MFC的精髓所在了。MFC 提供給應用程式使用的「很方便的介面」是兩組巨集。以Hello 的主視窗為例,第一個動作是在Example.H 加上DECLARE_MESSAGE_MAP:
DECLARE_MESSAGE_MAP()
下一個動作就用到Example.cpp中的一組巨集
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_COMMAND(IDM_ABOUT, OnAbout)
END_MESSAGE_MAP()
這樣訊息WM_PAINT 就順利匯入導到OnPaint 函式中了。但是他最後怎麼到他需要的函式中去的呢?這就是MESSAGE_MAP()的作用了。在這裡先不詳談。
下面就到下一個檔案ExampleDlg.cpp了
class CExampleDlg : public CDialog
{
// 構造
public:
CExampleDlg(CWnd* pParent = NULL); // 標準建構函式
// 對話方塊資料
enum { IDD = IDD_EXAMPLE_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支援
// 實現
protected:
HICON m_hIcon;
// 生成的訊息對映函式
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
};
看到註釋相信大家對這段程式碼已經有了基本的瞭解了,在這其中是一些函式的宣告包括建構函式,訊息對映函式,和非常重要的用於資料動態繫結的DoDataExchange()接下來我們來看ExampleDlg.cpp
BOOL CExampleDlg::OnInitDialog()
{
}
void CExampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
}
// 如果向對話方塊新增最小化按鈕,則需要下面的程式碼
// 來繪製該圖示。對於使用文件/檢視模型的 MFC 應用程式,
// 這將由框架自動完成。
void CExampleDlg::OnPaint()
{
}
cpp檔案是對.h檔案的函式的實現在這裡就不全部copy過來的,在這隻介紹最重要的三個函式
BOOL CExampleDlg::OnInitDialog()
呼叫這個成員函式是對WM_INITDIALOG訊息作出的反應。如果在對話方塊初始化後需要執行特別處理,覆蓋覆蓋該函式。首先呼叫基類OnInitDialog,但不考慮其返回值。正常情況下,覆蓋的函式返回TRUE。呼叫是通過標準的全域性對話方塊過程,而不是通過訊息對映。因此該函式不需要訊息對映入口。
void CExampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
這個函式主要是截獲控制命令的
void CExampleDlg::OnPaint()
OnPaint()是CWnd的類成員,負責響應WM_PAINT訊息。
下面就到了stdafx了
// stdafx.h : 標準系統包含檔案的包含檔案,
// 或是經常使用但不常更改的
// 特定於專案的包含檔案
#pragma once
#ifndef _SECURE_ATL
#define _SECURE_ATL 1
#endif
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // 從 Windows 頭中排除極少使用的資料
#endif
// 如果您必須使用下列所指定的平臺之前的平臺,則修改下面的定義。
// 有關不同平臺的相應值的最新資訊,請參考 MSDN。
#ifndef WINVER // 允許使用特定於 Windows XP 或更高版本的功能。 下面這段程式碼相信大家看註釋就能明白,那就是本程式對版本的要求
#define WINVER 0x0501 // 將此值更改為相應的值,以適用於 Windows 的其他版本。
#endif
#ifndef _WIN32_WINNT // 允許使用特定於 Windows XP 或更高版本的功能。
#define _WIN32_WINNT 0x0501 // 將此值更改為相應的值,以適用於 Windows 的其他版本。
#endif
#ifndef _WIN32_WINDOWS // 允許使用特定於 Windows 98 或更高版本的功能。
#define _WIN32_WINDOWS 0x0410 // 將它更改為適合 Windows Me 或更高版本的相應值。
#endif
#ifndef _WIN32_IE // 允許使用特定於 IE 6.0 或更高版本的功能。
#define _WIN32_IE 0x0600 // 將此值更改為相應的值,以適用於 IE 的其他版本。值。
#endif
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 建構函式將是顯式的
// 關閉 MFC 對某些常見但經常可放心忽略的警告訊息的隱藏
#define _AFX_ALL_WARNINGS
#include <afxwin.h> // MFC 核心元件和標準組件 下面的程式碼主要用來引用MFC各元件和類
#include <afxext.h> // MFC 擴充套件
#include <afxdisp.h> // MFC 自動化類
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC 對 Internet Explorer 4 公共控制元件的支援
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC 對 Windows 公共控制元件的支援
#endif // _AFX_NO_AFXCMN_SUPPORT
最後就剩下stdafx.cpp了,這段程式碼大家註釋簡單明瞭,在這裡就不做過多解釋了!
// stdafx.cpp : 只包括標準包含檔案的原始檔
// Example.pch 將作為預編譯頭
// stdafx.obj 將包含預編譯型別資訊
#include "stdafx.h"
看了這麼半天相信你對自動生成的程式碼已經有了一定的瞭解了吧!你是否找到編寫MFC的樂趣了呢?如果是的,那就關注一下我吧!讓我們一起學習,一起成長!