關於視窗程式

每個視窗都是特定視窗類別的成員。 視窗類別會決定個別視窗用來處理其訊息的預設視窗程式。 屬於相同類別的所有視窗都會使用相同的預設視窗程式。 例如,系統會定義下拉式方塊類別的視窗程式, (COMBOBOX) ;然後,所有下拉式方塊都會使用該視窗程式。

應用程式通常會註冊至少一個新的視窗類別及其相關聯的視窗程式。 註冊類別之後,應用程式可以建立該類別的許多視窗,全部都使用相同的視窗程式。 由於這表示數個來源可以同時呼叫相同的程式碼段,因此從視窗程式修改共用資源時,您必須小心。 如需詳細資訊,請參閱 視窗類別

對話方塊的視窗程式 (稱為對話方塊程式) 具有類似的結構和函式作為一般視窗程式。 本節中參考視窗程式的所有點也適用于對話方塊程式。 如需詳細資訊,請參閱 對話方塊

本節討論下列主題。

視窗程式的結構

視窗程式是具有四個參數並傳回帶正負號值的函式。 參數包含視窗控制碼、UINT訊息識別碼,以及使用WPARAM 和 LPARAM資料類型宣告的兩個訊息參數。 如需詳細資訊,請參閱 WindowProc

訊息參數通常包含其低序和高序字組的資訊。 應用程式可以使用數個宏從訊息參數擷取資訊。 例如, LOWORD 宏會從訊息參數擷取低序單字 (位 0 到 15) 。 其他宏包括 HIWORDLOBYTEHIBYTE

傳回值的解譯取決於特定訊息。 請參閱每個訊息的描述,以判斷適當的傳回值。

因為可以遞迴呼叫視窗程式,所以請務必將所使用的區域變數數目降到最低。 處理個別訊息時,應用程式應該在視窗程式外部呼叫函式,以避免過度使用區域變數,可能會導致堆疊在深層遞迴期間溢位。

預設視窗程式

DefWindowProc預設視窗程式函式會定義所有視窗共用的特定基本行為。 預設視窗程式會提供視窗的最小功能。 應用程式定義的視窗程式應該將它未處理的任何訊息傳遞至 DefWindowProc 函式以進行預設處理。

視窗程式子類別化

當應用程式建立視窗時,系統會配置記憶體區塊來儲存視窗特定的資訊,包括處理視窗訊息的視窗程式位址。 當系統需要將訊息傳遞至視窗時,它會搜尋視窗特定資訊以取得視窗程式的位址,並將訊息傳遞至該程式。

子類別化 是一種技術,可讓應用程式在視窗有機會處理訊息之前,攔截和處理傳送或張貼到特定視窗的訊息。 藉由子類別化視窗,應用程式可以增強、修改或監看視窗的行為。 應用程式可以將屬於系統全域類別的視窗子類別化,例如編輯控制項或清單方塊。 例如,應用程式可以將編輯控制項子類別化,以防止控制項接受特定字元。 不過,您無法將屬於另一個應用程式的視窗或類別子類別子類別化。 所有子類別化都必須在相同的進程中執行。

應用程式會將視窗的原始視窗程式位址取代為新視窗程式的位址,稱為 子類別程式,藉此將視窗子類別化。 之後,子類別程式會接收傳送或張貼到視窗的任何訊息。

子類別程式可以在收到訊息時採取三個動作:它可以將訊息傳遞至原始視窗程式、修改訊息,並將它傳遞給原始視窗程式,或處理訊息,而不傳遞至原始視窗程式。 如果子類別程式處理訊息,它可以在訊息傳遞至原始視窗程式之前、之後或兩者執行此動作。

系統提供兩種類型的子類別: 實例全域。 在 實例子類別化中,應用程式會取代視窗單一實例的視窗程式位址。 應用程式必須使用實例子類別化來子類別化現有的視窗。 在 全域子類別化中,應用程式會取代視窗類別 之 WNDCLASS 結構中的視窗程式位址。 使用 類別建立的所有後續視窗都有子類別程式的位址,但類別的現有視窗不會受到影響。

實例子類別化

應用程式會使用 SetWindowLong 函式,將視窗的實例子類別化。 應用程式會將 GWL_WNDPROC 旗標、視窗的控制碼傳遞給子類別,並將子類別程式的位址傳遞給 SetWindowLong。 子類別程式可以位於應用程式的可執行檔或 DLL 中。

SetWindowLong 會傳回視窗原始視窗程式的位址。 應用程式必須在 呼叫 CallWindowProc 函式的後續呼叫中儲存位址,以將攔截的訊息傳遞至原始視窗程式。 應用程式也必須有原始視窗程式位址,才能從視窗移除子類別。 若要移除子類別,應用程式會再次呼叫 SetWindowLong ,並將原始視窗程式的位址與 GWL_WNDPROC 旗標和控制碼傳遞至視窗。

系統擁有系統全域類別,控制項的各個層面可能會從一個版本的系統變更為下一個版本。 如果應用程式必須子類別屬於系統全域類別的視窗,開發人員可能需要在發行新版本的系統時更新應用程式。

因為實例子類別化會在建立視窗之後發生,所以您無法將任何額外的位元組新增至視窗。 子類別化視窗的應用程式應該使用視窗的屬性清單來儲存子類別化視窗實例所需的任何資料。 如需詳細資訊,請參閱 Window 屬性

當應用程式子類別化子類別化視窗時,它必須以執行子類別的反向順序移除子類別。 如果未反轉移除順序,可能會發生無法復原的系統錯誤。

全域子類別化

若要全域子類別視窗類別,應用程式必須具有類別視窗的控制碼。 應用程式也需要控制碼來移除子類別。 若要取得控制碼,應用程式通常會建立要子類別化之類別的隱藏視窗。 取得控制碼之後,應用程式會呼叫 SetClassLong 函式、指定控制碼、 GCL_WNDPROC 旗標,以及子類別程式的位址。 SetClassLong 會傳回 類別原始視窗程式的位址。

原始視窗程式位址會以與實例子類別處理中使用的相同方式,用於全域子類別化。 子類別程式會呼叫 CallWindowProc,將訊息傳遞至原始視窗程式。 應用程式會再次呼叫 SetClassLong ,並指定原始視窗程式的位址、 GCL_WNDPROC 旗標,以及子類別化類別之視窗的控制碼,以從視窗類別中移除子類別。 全域子類別控制項類別的應用程式必須在應用程式終止時移除子類別;否則,可能會發生無法復原的系統錯誤。

全域子類別處理的限制與實例子類別化相同,再加上一些額外的限制。 應用程式不應該針對 類別或視窗實例使用額外的位元組,而不知道原始視窗程式如何使用它們。 如果應用程式必須將資料與視窗產生關聯,它應該使用視窗屬性。

視窗程式超類別化

超類別化 是一種技術,可讓應用程式建立具有現有類別基本功能的新視窗類別,以及應用程式所提供的增強功能。 超級類別是以稱為 基類的現有視窗類別為基礎。 基類通常是系統全域視窗類別,例如編輯控制項,但可以是任何視窗類別。

superclass 有自己的視窗程式,稱為 superclass 程式。 超級類別程式可以在收到訊息時採取三個動作:它可以將訊息傳遞至原始視窗程式、修改訊息,並將它傳遞給原始視窗程式,或處理訊息,而不傳遞至原始視窗程式。 如果超類別程式處理訊息,它可以在訊息傳遞至原始視窗程式之前、之後或兩者執行此動作。

不同于子類別程式,超級類別程式可以處理視窗建立訊息 (WM_NCCREATEWM_CREATE等) ,但也必須將它們傳遞至原始基類視窗程式,讓基類視窗程式可以執行其初始化程式。

若要將視窗類別超類別,應用程式會先呼叫 GetClassInfo 函 式來擷取基類的相關資訊。 GetClassInfo會以基類WNDCLASS結構中的值填入WNDCLASS結構。 接下來,應用程式會將自己的實例控制碼複製到WNDCLASS結構的hInstance成員中,並將超級類別的名稱複製到lpszClassName成員。 如果基類有功能表,應用程式必須提供具有相同功能表識別碼的新功能表,並將功能表名稱複製到 lpszMenuName 成員。 如果超級類別程式處理 WM_COMMAND 訊息,而且不會將它傳遞給基類的視窗程式,功能表就不需要有對應的識別碼。 GetClassInfo不會傳回WNDCLASS結構的lpszMenuNamelpszClassNamehInstance成員。

應用程式也必須設定WNDCLASS結構的lpfnWndProc成員。 GetClassInfo函式會將類別原始視窗程式的位址填入此成員。 應用程式必須儲存此位址,才能將訊息傳遞至原始視窗程式,然後將超級類別程式的位址複製到 lpfnWndProc 成員。 如有必要,應用程式可以修改 WNDCLASS 結構的任何其他成員。 在填滿 WNDCLASS 結構之後,應用程式會將 結構的位址傳遞至 RegisterClass 函式,以註冊超級類別。 然後,可以使用 superclass 來建立視窗。

因為超類別化會註冊新的視窗類別,所以應用程式可以同時新增至額外的類別位元組和額外的視窗位元組。 超級類別不得針對基類或視窗使用原始的額外位元組,因為實例子類別或全域子類別不應該使用這些位元組。 此外,如果應用程式將額外的位元組用於類別或視窗實例,則必須參考相對於原始基類所使用的額外位元組數目的額外位元組數目。 因為基類所使用的位元組數目可能會從基類的一個版本到下一個版本不同,所以超類別本身額外位元組的起始位移也可能從基類的一個版本變更為下一個版本。