MFC實現原理剖析
“現在已經是人工智慧、大資料的時代,雲+端才是王道,桌面程式設計已經過時了,還有沒有必要學習MFC?”
這是許多困擾剛剛入行朋友的問題,不可否認,由於python、Java等開發語言和環境的流行,Visual C++的應用範圍也相應縮小。
“有人說現在c++越來越接近邊緣性語言?c++程式設計師以後的發展方向在那裡,我學習MFC會不會被淘汰?”
的確C++作為普及性應用程式設計語言的地位已經不再,但是它作為系統程式設計語言的地位沒變。 筆者個人的淺見在於:一個系統程式設計師的核心優勢之一就是對計算機裝置的透徹理解。在筆者求學階段,筆者的導師曾經有過這樣的指導,對於本人的影響非常深刻:
“你們覺得你們學計算機這個專業最大的優勢是什麼?是會程式設計序嗎?會寫演算法嗎?”
“論寫演算法,你們不如數學方向的同學,他們天天接受邏輯思維訓練,抽象能力的培養,你們不佔優勢”
“論寫業務邏輯,比如資訊管理系統,你們不如有行業經驗的懂開發技術的人員,因為你們在業務理解上不佔優勢”
“寫操作硬體,你們不如寫自動化,機電一體化的,不如通訊的,他們理解協議,用程式碼指揮硬體的能力比你們也要強”
“那麼,計算機專業的核心優勢在哪裡?”
“我認為一定是在你們對整個計算機裝置的理解,這個才是你們要強化的技能核心”
正是因為此,筆者才對進入行業領域的C++學習者不斷建議:從各種角度提升自己的對計算機裝置的核心理解。
那麼理解計算機裝置的一種可選路徑在哪裡呢?筆者認為莫過於對作業系統的學習和探索。毫無疑問,當前在PC市場中最為人們所熟知的作業系統就是windows了。對於普通使用者來說,當他雙擊word圖示,啟動word,開始打字,排版,插入圖片的時候,他一定認為是自己在處理辦公業務。當這個使用者按下鍵盤a,打出一個字母,按下滑鼠,圈出一段文字,他一定認為是自己“在寫,在畫”這個內容。但是作為一名開發人員來說,你又是如何理解這種行為呢?
實際上,真正在寫,在畫的並不是這位使用者,而是word這個程式。從執行原理來說,恐怕這樣的描述更加精準。
其實,這個呈現出來的過程中word與windows作業系統互動被使用者從邏輯上忽略了。而我們程式開發人員的任務實際上是在兩端程式設計
一方面,我們接受使用者的輸入,讓作業系統感知到我們應用程式的存在,並將其做相應的處理邏輯;
另一方面,我們將處理好的邏輯通過windows作業系統的幫助以友好的方式呈現給使用者。
這兩個方面都涉及到編寫程式碼的工作,這才是我們編碼的邏輯所在。
接下來的問題就是,windows感知到使用者的輸入好理解,那麼如何理解windows感知到應用程式的存在呢?還是以word為例
打個比方,就像一所學校,每一個學員都有一個代號供學校管理排程,要求你聽課,考試。有了hwnd這個概念,我們就知道了,同樣,windows作業系統需要顯示,銷燬這個窗體,都需要通過hwnd來操作。我們程式設計的時候,就可以把自己需要操作的hwnd給windows作業系統,讓windows作業系統實現我們的目的。很顯然,一個hwnd是一個視窗的表示,同樣的,呈現一個視窗肯定需要一套複雜的結構,就好比一個在學校的學生,有學號,有出生年年月,有性別,有專業,各種各樣的分量資訊。一個視窗結構,肯定也是一個複雜的分量組合。對應到語言層面,一定是一個C語言的struct的結構。
C語言是開發作業系統的核心語言,很顯然,Windows API是面向C語言風格的。大家都知道C++是C語言的超集,微軟為了方便當時的開發人員將已經存在的C語言開發方式,封裝成了一套類庫,這個就是MFC的由來,我們可以認為MFC是Windows C++的API。於是,這個就帶來了一個新的問題:如何用C++的語義來替代C風格的開發方式。
最直接的方式就是建立面向物件的語義到實際開發概念的對映,實質今日,許多老的軟體需要維護的工作,依然會從msdn中查詢mfc的類結構,其中最重要的一個結構就是CWnd,這個又稱為視窗類,我們來看下微軟是怎麼做的。
class CWnd :public CCmdTarget {
DELCARE_DYNCREATE(CWnd)
public:
CWnd();
virtual ~CWnd();
HWND m_hWnd;
operator HWND() const { return m_hWnd; }
HWND GetSafeHwnd() { return this == NULL ? NULL : m_hWnd; }
//視窗控制代碼對映
static CWnd* FromeHandle(HWND hWnd);
static CWnd* FormHandlePermanet(HWND hWnd);
BOOL Attach(HWND hWndNew);
HWND Detach();
…}
在C++的語義看來,用來互動的一定是一系列的物件,這些物件與物件之間的互動運動完成軟體的工作過程。比如我們做這樣一個想象,圖示中是記憶體中已經存在的一系列物件,這些物件之間相互呼叫,只要合理安排好這些呼叫的先後秩序,同時這些物件的資料進行加工處理,得到結果就是這個軟體系統執行過程。
藉助這樣的思想,微軟在改造C語言的程式設計風格的時候,他就要考慮概念的轉移,以前,程式原只需要操作hwnd就可以直接和作業系統互動,現在則不然,首先,要刻畫一個c++的觀念與hwnd對應。這個觀念的就是CWnd類設計的初衷。CWnd是批量製造窗體物件的類。他可以批量的new 出一堆窗體物件來。
首先,CWnd物件就必須有Hwnd,這個就是
設計的由來。
那麼,這些函式又是什麼設計語義呢?
這個就要談MFC的體系了。
我們首先看下擁有了mfc之後,應用程式與作業系統的交互發生了什麼樣的變化:
在沒有MFC的時候,所有的應用程式的訊息都是直接和windows系統打交道。而MFC應用程式則不然,它像一個楔子一樣載入了應用程式與作業系統之間。如果我們寫一個mfc的word,所有的windows訊息的截獲和感知,都是通過mfc來完成的。這就好像以前你可以直接和老闆對話,而現在你的所有的請求和應答都只能委託給mfc來操作。
有了這個概念,再理解上面的程式碼就順暢了。以前我們自己的應用程式要提請windows作業系統操作,直接訪問hwnd就可以了。但是現在我們按照c++語法,我們用的是Cwnd物件。這個CWnd是一個C++物件,而且是MFC創造出來的c++物件,並不是windows的物件。所以,MFC有責任管理好這個物件,這個物件的出生與消亡都跟隨的是C++語義。因此,當我們需要一個表達一個窗體的概念的時候,我們很自然的就會new一個Cwnd的物件出來。但是,這個CWnd物件是有風險的——如果這個CWnd物件先於windows的窗體出現怎麼辦?(CWnd物件的記憶體已經構建好了,但是hwnd還為空,所以它的m_hWnd為空)。同樣的CWnd物件概念的出現將windows窗體的hWnd概念和CWnd的概念進行了剝離,那麼從理論上講CWnd物件可以結合任意的hWnd物件。(只要將m_hWnd賦予不同的值就可以了)。
這又是MFC的一個高超之處,就是要將hWnd和CWnd進行觀念解耦,當應用程式需要用CWnd物件的觀念表達Windows窗體的時候,用引數傳入就可以了。以BOOL Attach(HWND hWndNew);為例,程式開發人員大可以將一個已經存在的windows窗體的hwnd傳給當前的CWnd物件,通過attch方法,靈活的將windows窗體附著在Cwnd這個C++物件上。這種設計技巧是十分精妙的,既可以在不變更已有系統的情況下,靈活的用C++的方法進行開發。