1. 程式人生 > >程式設計學習筆記之MFC內部組織架構

程式設計學習筆記之MFC內部組織架構

      MFC全稱是Microsoft Foundation Classes,意為微軟基礎類庫,是一個C++的類庫,裡面封裝了大量的windows API尤其是win32 API函式,因為我們在開發win32應用程式的時候,需要做很多重複的步驟,比如註冊視窗類、初始化視窗、創造視窗、更新視窗等,所以為了省卻不必要的麻煩,微軟將這些步驟打包封裝成一個框架,裡面有win32所必需的元件,這個“框架”就叫MFC。

      MFC源於win32,要弄清它們之間的關係,我們首先要理解MFC object與windows object之間的區別

1):MFC object是由c++的CWnd或其派生類定義的,在程式執行時由類中的建構函式建立,然後在執行類中解構函式的時候銷燬。而windows object是window系統的一個內部資料結構例項,它由一個視窗控制代碼來引用,window系統為它分配系統資源,它是在MFC視窗建立之後建立的,一般由CWnd類中的Create函式所建立,得到的視窗控制代碼儲存在CWnd的m_hWnd成員變數中,它可以被一個程式或使用者的動作銷燬【呼叫相應的sdk系統函式】。

2):windows object是屬於低層的,而MFC object是高層的,後者封裝了前者大部分的功能,MFC object的使用者不需要直接應用windows object的控制代碼來使用win32 API,而是用對應的MFC object函式來代替。我們可以從MFC object那裡得到一個對應的windows object控制代碼,比如使用GetSafeHandle函式,也可以直接拿windows object裡的控制代碼來建立一個新的MFC object,如果使用MFC的FromHandle函式,得到的是一個臨時的MFC object物件,而如果使用的是Attach,則會得到一個永久性物件。

3):MFC object對系統的其它程序是不可見的,而windows object則是全域性性的,後者一旦建立,那麼它的控制代碼便可以被系統的其它程序所使用,比如,A程序如果想對B程序傳送訊息,便可以通過獲取B程序的視窗控制代碼來完成。

【宣告:以上內容“借鑑”自百科詞條,說是抄襲也不為過,笑。。。】

      接下來我們談談如何建立一個簡單的MFC應用程式,本文以visual studio 2013為開發平臺,首先開啟vs2013,選擇檔案->新建->專案,接著選擇visual C++下的MFC應用程式,檔名稱我們取名叫test,點選下一步,在嚮導裡面我們選擇單文件,點選完成。至此,我們就生成了一個自己的MFC應用程式。按下F5會出現如此介面:

這個就是我們自己的MFC應用程式。

      我們再來看一下它的組織架構:

我們可以看到,這裡面一共有5個類,其中test.cpp、testDoc.cpp與testView.cpp名字裡面的“test”都是根據我們生成MFC應用程式時所取的名字來命名的,而無論我們給專案取什麼名字,MainFrm.cpp和stdafx.cpp這兩個類的名字是不會變得!我們雙擊一下test.cpp類,進入它的定義,如圖:

我們發現,CtestApp是繼承自CWinApp的,並且在這裡【第54行】聲明瞭一個CtestApp型別的全域性變數theApp,這樣當我們按下F5執行的時候,程式會首先構造一個全域性變數theApp,然後因為構造子類例項會先執行父類的建構函式這個原理,程式再去執行CWinApp的建構函式【在CWinApp之前還繼承自CWinThread,然後CWinThread上面還有父類,因為關係太過複雜,這裡不再展開】,在這些都完成之後,然後才由MainCRTStartUp呼叫程式的主函式_tWinMain【這個_tWinMain是一個巨集定義,其本來面目就是WinMain】,再然後在這個主函式_tWinMain裡面,會呼叫一個AfxWinMain,AfxWinMain函式原始碼如下:

    int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,   
    LPTSTR lpCmdLine, int nCmdShow)   
    {   
        ASSERT(hPrevInstance == NULL);   
        int nReturnCode = -1;   
        CWinThread* pThread = AfxGetThread();   
        CWinApp* pApp = AfxGetApp();  
        // AFX internal initialization   
        if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))   
            goto InitFailure;  
        // App global initializations (rare)   
        if (pApp != NULL && !pApp->InitApplication())   
            goto InitFailure;  
        // Perform specific initializations   
        if (!pThread->InitInstance())   
        {   
            if (pThread->m_pMainWnd != NULL)   
            {   
                TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");   
                pThread->m_pMainWnd->DestroyWindow();   
            }   
            nReturnCode = pThread->ExitInstance();   
            goto InitFailure;   
        }   
        nReturnCode = pThread->Run();  
    InitFailure:   
    #ifdef _DEBUG   
        // Check for missing AfxLockTempMap calls   
        if (AfxGetModuleThreadState()->m_nTempMapLock != 0)   
        {   
            TRACE1("Warning: Temp map lock count non-zero (%ld).\n",   
                AfxGetModuleThreadState()->m_nTempMapLock);   
        }   
        AfxLockTempMaps();   
        AfxUnlockTempMaps(-1);   
    #endif  
        AfxWinTerm();   
        return nReturnCode;   
    }  

注意第6行和第7行的程式碼,程式首先聲明瞭一個CWinThread型別的物件pThread,然後又聲明瞭一個CWinApp的物件pApp,並分別呼叫AfxGetThread與AfxGetApp函式為它們賦值【所有帶有字首Afx的函式,都表示這是一個全域性函式】,事實上,這兩個函式所返回的都是我們前面那個theApp物件的指標,即一個CtestApp型別的物件指標。瞭解這點之後,再來後面的程式碼就容易理解了,程式就是利用pThread與pApp這兩個物件,完成一個視窗類的設計、註冊、建立、顯示更新等步驟。先說註冊,在18行程式碼中,通過pThread->InitInstance()呼叫了CWinThread類中的方法InitInstance,其實這個InitInstance是一個虛擬函式,這就意味著會回到theApp的InitInstance中執行,在這裡面,它會利用一個叫AfxEndDeferRegisterClass()來設計註冊視窗類,MFC預先為我們設計了一些視窗類,然後在這裡根據我們的需要為我們註冊一下。接下來就是建立視窗了,那麼程式會在哪裡建立這個註冊好的視窗呢?答案是類CMainFrame中的PreCreateWindow(),再接著跳轉到父類CFrameWnd中PreCreateWindow方法中,利用裡面的Create呼叫CreateEx來建立視窗【用了兩個CreateEx函式互相呼叫,它們是過載的關係,即引數列表不同】。再接著就是顯示和更新視窗,分別通過theApp物件裡面的InitInstance函式呼叫來完成。最後的最後,就是我們的訊息迴圈,看第25行程式碼,通過呼叫pThread中的Run方法來實現對外部訊息的迴圈響應,因為pThread實際是一個指向theAPP物件的指標,而在theApp的實際型別是CtestApp,可是在這個型別的定義中,並沒有關於Run方法的定義,這是因為這個Run方法是若干個上層父類CWinThread中的虛擬函式,所以程式到這裡就回到了CWinThread類中的Run方法中,讓我們看一下Run方法的定義:

其中紅框內的函式呼叫,就是有關訊息路由等動作了。(未完待續)