從上一篇我轉載如何進行Windows SDK編程開始,我希望可以藉此補充一下Windows編程的一些背景知識。之所以這樣,是因為在我前面介紹“SW系統的視窗類別”時,假設了讀者對Windows介面編程已經有一定的瞭解。
上一篇主要從介紹“如何用”的角度闡述Windows編程。但是我個人習慣“打破沙鍋問到底”,很多東西是靠“悟”,而不是“記”。所以這一篇我們聊聊Windows SDK為何會是如今這個樣子的。
對於一個經典的WinMain函數,通常包含三步:
- 註冊視窗類別(RegisterClass)。
- 建立並顯示視窗(CreateWindow and ShowWindow)。
- 訊息迴圈(MessageLoop)。即:取得訊息 -> 指派訊息 -> 處理訊息。
視窗程序需要“建立並顯示視窗”,這顯而易見。關於“訊息迴圈”也容易理解,並且我們在“SW系統簡介”中描述已經得非常詳細。
我相信最令人迷惑的是:“視窗類別”是什麼概念?為什麼需要RegisterClass?
有人回答:“視窗類別”是同類視窗的公用屬性,是這一類視窗的共用資料。
有人回答:“視窗類別”是同類視窗的預設資料(屬性)。
回答視窗類別資料是共用資料的,錯誤。因為我們知道每一個視窗都有自己獨立的菜單、表徵圖、視窗過程(WindowProc,這個最重要了)等等。它們並不存在共用關係。回答是預設資料(屬性)的,正確,但沒有回答為什麼需要預設屬性,更沒涉及到更為重要的原因。
我們先來看看CreateWindow與CreateDialog的原型:
HWND CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
HWND CreateDialog(
HINSTANCE hInstance,
LPCTSTR lpTemplate,
HWND hWndParent,
DLGPROC lpDialogFunc
);
為什麼普通視窗(Window)的建立不是象對話方塊(Dialog)一樣,直接把視窗過程(WindowProc)傳進去,甚至把其他視窗類別相關的資料全部直接在CreateWindow時傳入?也許有人回答說,這是為了減少CreateWindow的參數個數。——呵呵,我還真不知道怎麼去證明這種說法是錯的。但是我固執的認為我下面給出的理由要充分些。
聽說過序列化(Searialize)技術嗎?有讀者馬上回答:知道,這是MFC中把對象寫入磁碟和從磁碟中讀入並還原出這些對象的技術。這個回答我給它4分(滿分5分)。是的,序列化(Searialize)是對象持久化和還原的技術。但這不是MFC才開始有,而是DOS下Turbo Vision就已經有的一個技術。
對話方塊是什嗎?對話方塊其實是支援了序列化(Searialize)技術的特殊視窗,它在初始化的時候,從資源中還原(建立)出它的各個子視窗。只是,與普通序列化(Searialize)不太一樣的是,對象持久化過程不是對話方塊做,而是對話方塊編輯器負責的。
無疑,基於對話方塊進行可視化編程是相當友好的。那麼,從支援這種可視化編程的角度來看,我們需要支援序列化(Searialize),需要從磁碟中建立視窗。我們再回頭看看CreateWindow函數的原型,你將發現,這些參數是“可序列化(可存檔)”的。而且,你立刻意識到,視窗過程(WindowProc)是不可序列化的。
我們知道,序列化技術需要RuntimeClass技術進行對象的動態建立。而所謂的RuntimeClass技術,無非是建立了類唯一標識(如類名、GUID等)到類建立函數(其他的附屬資料是比較次要的)的映射而已。
現在到了關鍵:為了支援從磁碟還原視窗對象,Windows需要引入視窗類別名,並建立它與視窗過程(WindowProc)的映射。——這正是RegisterClass存在的意義。而RegisterClass中其他的視窗類別屬性是次要的,並且也許前面說得沒錯,這些屬性的存在,只是為了減少CreateWindow函數的參數個數。
補充:點擊這裡瞭解RuntimeClass與Searialize的實現機理。
補充:“紳士亦花心”提到了GDI資源的共用問題。應當承認,我前面說“視窗類別資料是共用資料”這種說法錯誤,是比較武斷的說法。是的,Windows必須面對GDI資源的共用問題,不同視窗亦存在資源共用的事實。更為合理的說法是:每一個視窗可以獨立設定自己的菜單、表徵圖、視窗過程,但菜單/表徵圖等GDI資源是可以共用的。因為我們知道,Windows對GDI資源的共用策略是,視窗不擁有GDI資源(菜單/表徵圖等)的所有權,使用者必須為GDI資源的生命週期負責。換句話說,視窗只擁有菜單/表徵圖控制代碼。