每個窗口類別都有相同類別之所有視窗共用的相關聯視窗程式。 視窗程式會處理該類別所有視窗的訊息,因此會控制其行為和外觀。 如需詳細資訊,請參閱 視窗程式。
進程必須先註冊窗口類別,才能建立該類別的視窗。 註冊視窗類別會將視窗程序、類別樣式和其他類別屬性與類別名稱產生關聯。 當進程在 CreateWindow 或 CreateWindowEx 函式中指定類別名稱時,系統會使用與該類別名稱相關聯的視窗程式、樣式和其他屬性來建立視窗。
本節討論下列主題。
窗口類別的類型
視窗類別有三種類型:
這些類型在範圍、註冊和銷毀的時機及方式上有所不同。
系統類別
系統類別是系統註冊的窗口類別。 許多系統類別可供所有進程使用,而其他類別則僅供系統內部使用。 因為系統會註冊這些類別,所以進程無法終結它們。
系統會在其中一個線程第一次呼叫 User 或 Windows Graphics Device Interface (GDI) 函式時,為進程註冊系統類別。
每個應用程式都會接收自己的系統類別複本。 在相同的 VDM 中,所有 16 位元的 Windows 應用程式會共用系統類別,就像在 16 位元 Windows 上的一樣。
下表描述可供所有進程使用的系統類別。
班級 | 說明 |
---|---|
按鈕 | 按鈕的類別。 |
ComboBox | 組合框的類別。 |
編輯 | 編輯控制項的類別。 |
清單框 | 清單框的類別。 |
MDIClient | MDI 用戶端視窗的類別。 |
捲軸 | 捲軸的類別。 |
靜態的 | 靜態控件的類別。 |
下表描述僅可供系統使用的系統類別。 為了完整起見,它們列在這裡。
班級 | 說明 |
---|---|
ComboLBox | 下拉式方塊中包含的清單框類別。 |
DDEMLEvent | 動態數據交換管理庫 (DDEML) 事件的類別。 |
訊息 | 僅限訊息視窗的類別。 |
#32768 | 功能表的類別。 |
#32769 | 桌面視窗的類別。 |
#32770 | 對話框的類別。 |
#32771 | 工作切換視窗的類別。 |
#32772 | 圖示標題的類別。 |
應用程式的全域類別
應用程式全域類別是由可執行檔或 DLL 所註冊的視窗類別,可供進程中所有其他模組使用。 例如,您的 .dll 可以呼叫 RegisterClassEx 函式來註冊將自定義控件定義為應用程式全域類別的窗口類別,以便載入 .dll 的程式可以建立自定義控件的實例。
若要建立可在每個進程中使用的類別,請在 .dll 中建立窗口類別,並在每個進程中載入 .dll。 若要在每個進程中載入 .dll,請在下列登錄機碼中將其名稱新增至 AppInit_DLLs 值:
HKEY_LOCAL_MACHINE\軟體\Microsoft\Windows NT\CurrentVersion\Windows
每當進程啟動時,系統會在新啟動的進程內容中載入指定的 .dll,再呼叫其進入點函式。 .dll 必須在初始化程式期間註冊 類別,而且必須指定 CS_GLOBALCLASS 樣式。 如需詳細資訊,請參閱 類別樣式。
若要移除應用程式全域類別並釋放與其相關聯的記憶體,請使用 UnregisterClass 函式。
應用程式中的本地類別
應用程式本機類別是可執行檔或 .dll 為其獨佔用途而註冊的任何視窗類別。 雖然您可以註冊任意數目的本機類別,但通常只註冊一個。 這個視窗類別支援應用程式主視窗的視窗程式。
當註冊它的模組關閉時,系統會刪除這個本機類別。 應用程式也可以使用 UnregisterClass 函式來移除本機類別,並釋放與其相關聯的記憶體。
系統如何找出窗口類別
系統會針對三種視窗類別的每個類型維護結構清單。 當應用程式呼叫 CreateWindow 或 CreateWindowEx 函式來建立具有指定類別的視窗時,系統會使用下列程式來尋找類別。
- 搜尋應用程式本機類別清單中,具有與模組實例句柄匹配的指定名稱的類別。 (數個模組可以使用相同名稱在相同進程中註冊本機類別。
- 如果名稱不在應用程式本機類別清單中,請搜尋應用程式全域類別的清單。
- 如果名稱不在應用程式全域類別清單中,請搜尋系統類別清單。
應用程式建立的所有視窗都會使用此程式,包括系統代表應用程式建立的視窗,例如對話框。 可以覆寫系統類別,而不會影響其他應用程式。 也就是說,應用程式可以註冊與系統類別同名的應用程式本機類別。 這會取代應用程式內容中的系統類別,但不會防止其他應用程式使用系統類別。
註冊視窗類別
視窗類別會定義視窗的屬性,例如其樣式、圖示、游標、功能表和視窗程式。 註冊窗口類別的第一個步驟是使用視窗類別資訊填入 WNDCLASSEX 結構。 如需詳細資訊,請參閱 Window 類別的元素。 接下來,將 結構傳遞至 RegisterClassEx 函式。 如需詳細資訊,請參閱 使用窗口類別。
若要註冊應用程式全域類別,請在WNDCLASSEX結構的樣式成員中指定CS_GLOBALCLASS樣式。 註冊應用程式本機類別時,請勿指定 CS_GLOBALCLASS 樣式。
如果您使用 RegisterClassEx 的 ANSI 版本 RegisterClassExA 註冊視窗類別,應用程式會要求系統使用 ANSI 字元集將訊息的文字參數傳遞至所建立類別的視窗;如果您使用 RegisterClassEx、RegisterClassExW 的 Unicode 版本註冊類別,應用程式會要求系統使用 Unicode 字元集將訊息的文字參數傳遞給所建立類別的視窗。 IsWindowUnicode 函式可讓應用程式查詢每個窗口的性質。 如需 ANSI 和 Unicode 函式的詳細資訊,請參閱 函式原型的慣例。
註冊類別的可執行檔或 DLL 是 類別的擁有者。 系統會從類別註冊時,傳遞至 RegisterClassEx 函數的 WNDCLASSEX 結構中的 hInstance 成員,來判斷類別的所有權。 對於 DLL,hInstance 成員必須是 .dll 實例的控制代碼。
當擁有它的 .dll 被卸載時,類別不會被銷毀。 因此,如果系統針對該類別的視窗呼叫視窗程式,則會導致存取違規,因為包含視窗程式的 .dll 不再位於記憶體中。 在卸除 .dll 之前,進程必須使用 類別終結所有視窗,並呼叫 UnregisterClass 函式。
Window 類別的要素
視窗類別的元素會定義屬於 類別之視窗的預設行為。 註冊窗口類別的應用程式會藉由在 WNDCLASSEX 結構中設定適當的成員,並將結構傳遞至 RegisterClassEx 函式,將元素指派給 類別。 GetClassInfoEx 和 GetClassLong 函式會擷取指定視窗類別的相關信息。 SetClassLong 函式會修改應用程式已註冊的本機或全域類別的元素。
雖然完整的視窗類別是由許多元素所組成,但系統只需要應用程式提供類別名稱、視窗程式位址和實例句柄。 使用其他元素來定義類別視窗的預設屬性,例如游標的形狀和視窗功能表的內容。 您必須將 WNDCLASSEX 結構的任何未使用成員初始化為零或 NULL。 視窗類別元素如下表所示。
元素 | 目標 |
---|---|
類別名稱 | 區分類別與其他已註冊類別。 |
視窗程式位址 | 函式的指標,該函式會處理傳送至 類別中視窗的所有訊息,並定義窗口的行為。 |
實例控制代碼 | 識別註冊該類別的應用程式或 .dll。 |
類別游標 | 定義系統針對類別視窗顯示的滑鼠游標。 |
類別圖示 | 定義大型圖示和小型圖示。 |
類別背景筆刷 | 定義當視窗被開啟或繪製時填滿客戶區域的色彩和圖樣。 |
課程選單 | 指定未明確定義功能表之視窗的預設功能表。 |
類別樣式 | 定義如何在移動或調整視窗大小之後更新視窗、如何處理滑鼠按兩下、如何配置裝置內容的空間,以及視窗的其他層面。 |
進階類別記憶體 | 指定系統應該保留給 類別的額外記憶體數量,以位元組為單位。 類別中的所有視窗都會共用額外的記憶體,並可用於任何應用程式定義的用途。 系統會將此記憶體初始化為零。 |
額外的視窗記憶體 | 指定系統應該針對屬於 類別的每個視窗保留的額外記憶體數量,以位元組為單位。 額外的記憶體可用於任何應用程式定義的用途。 系統會將此記憶體初始化為零。 |
類別名稱
每個窗口類別都需要 類別名稱 ,才能區分一個類別與另一個類別。 將WNDCLASSEX結構的 lpszClassName 成員設定為指定名稱之 Null 終止字串的位址,以指派類別名稱。 因為視窗類別是進程特定的,因此視窗類別名稱只能在相同的進程中是唯一的。 此外,因為類別名稱佔用系統私用 Atom 資料表中的空間,因此您應該盡可能保持類別名稱字串簡短。
GetClassName 函式會擷取指定視窗所屬類別的名稱。
窗口程序地址
每個類別都需要一個視窗程式位址,以定義用來處理類別中視窗之所有訊息之視窗程序的進入點。 當系統要求窗口執行工作時,系統會將訊息傳遞給程式,例如繪製其工作區或回應用戶的輸入。 過程會透過將地址複製到 WNDCLASSEX 結構的 lpfnWndProc 成員,將視窗程序指派給類別。 如需詳細資訊,請參閱 視窗程式。
實例控制代碼
每個窗口類別都需要實例句柄來識別註冊類別的應用程式或 .dll。 系統需要實例句柄來追蹤所有模組。 系統會將控制代碼指派給正在運行的可執行程序的每個副本,或 .dll。
系統會將實例句柄傳遞至每個可執行文件的進入點函式(請參閱 WinMain)和 .dll(請參閱 DllMain)。 可執行檔或 .dll 將這個實例句柄複製到 WNDCLASSEX 結構的 hInstance 成員,以指派給 類別。
類別游標
類別游標會定義當游標位於類別中視窗的客戶區域時的形狀。 當游標進入視窗的工作區時,系統會自動將游標設定為指定的圖形,並確保該圖形保留在工作區中。 若要將游標圖形指派給窗口類別,請使用LoadCursor函式載入預先定義的游標圖形,然後將傳回的數據指標句柄指派給WNDCLASSEX結構的 hCursor 成員。 或者,提供自定義游標資源,並使用 LoadCursor 函式從應用程式資源載入。
系統不需要類游標。 如果應用程式將 WNDCLASSEX 結構的 hCursor 成員設定為 NULL,則不會定義任何類別數據指標。 系統會假設視窗會在每次游標移至視窗時設定游標圖形。 每當視窗收到WM_MOUSEMOVE訊息時,視窗就可以呼叫 SetCursor 函式來設定游標圖形。 如需游標的詳細資訊,請參閱 游標。
類別圖示
類別圖示是系統用來代表特定類別視窗的圖片。 應用程式可以有兩個類別圖示:一個大圖示和一個小圖示。 系統會在使用者按下 ALT+TAB 時出現的任務切換視窗中,以及任務列和總管的大型圖示檢視中顯示視窗 的大型類別圖示 。 小型類別圖示會出現在視窗的標題欄中,以及任務列和總管的小型圖示檢視中。
若要將大型和小型圖示指派給窗口類別,請在 WNDCLASSEX 結構的 hIcon 和hIconSm 成員中指定圖示的句柄。 圖示維度必須符合大型和小型類別圖示的必要維度。 針對大型類別圖示,您可以藉由指定呼叫 GetSystemMetrics 函式中的SM_CXICON和SM_CYICON值,來判斷所需的維度。 針對小型類別圖示,指定 SM_CXSMICON 和 SM_CYSMICON 值。 如需詳細資訊,請參閱 圖示。
如果應用程式將 WNDCLASSEX 結構的 hIcon 和 hIconSm 成員設定為 NULL,則系統會使用預設應用程式圖示做為視窗類別的大小型類別圖示。 如果您指定大型類別圖示,但不是小型類別圖示,系統會根據大型類別圖示建立小型類別圖示。 不過,如果您指定小型類別圖示而非大型類別圖示,則系統會使用預設應用程式圖示做為大型類別圖示,而指定的圖示則為小型類別圖示。
您可以使用 WM_SETICON 訊息來覆寫特定視窗的大或小類別圖示。 您可以使用 WM_GETICON 訊息來擷取目前的大型或小型類別圖示。
類別背景筆刷
類別背景筆刷會準備視窗的客戶端區域,以供應用程式後續繪製。 系統會使用筆刷將工作區填滿純色或圖樣,藉此移除該位置的所有先前影像,不論它們是否屬於視窗。 系統會通知視窗,應透過將 WM_ERASEBKGND 訊息傳送至視窗來繪製其背景。 如需詳細資訊,請參閱 筆刷。
若要將背景筆刷指派給類別,請使用適當的 GDI 函式建立筆刷,並將傳回的筆刷句柄指派給 WNDCLASSEX 結構的 hbrBackground 成員。
應用程式可以將 hbrBackground 成員設定為標準系統色彩值之一,而非建立新的筆刷。 如需標準系統色彩值的清單,請參閱 SetSysColors。
若要使用標準系統色彩,應用程式必須將背景色彩值增加一個。 例如, COLOR_BACKGROUND + 1 是系統背景色彩。 或者,您可以使用 GetSysColorBrush 函式來擷取對應至標準系統色彩的筆刷把柄,接著在 WNDCLASSEX 結構的 hbrBackground 成員中指定該把柄。
系統不需要窗口類別具有類別背景筆刷。 如果此參數設定為 NULL,則每當視窗收到 WM_ERASEBKGND 訊息時,都必須繪製自己的背景。
類別選單
類別功能表會定義在建立視窗時沒有明確功能表時,類別中視窗要使用的預設功能表。 功能表是命令清單,使用者可以從中選擇要執行應用程式的動作。
您可以將 WNDCLASSEX 結構的 lpszMenuName 成員設定為指定功能表資源名稱的 Null 終止字串位址,將功能表指派給類別。 功能表假設為指定應用程式中的資源。 系統會在需要時自動載入功能表。 如果功能表資源是由整數識別,而不是以名稱識別,應用程式可以在指派值之前套用MAKEINTRESOURCE巨集,將 lpszMenuName 成員設定為該整數。
系統不需要類別功能表。 如果應用程式將 WNDCLASSEX 結構的 lpszMenuName 成員設定為 NULL,則 類別中的視窗沒有功能表列。 即使沒有提供類別功能表,應用程式仍可在建立視窗時定義視窗的功能表欄。
如果為類別提供功能表,而且該類別的子視窗已建立,則會忽略功能表。 如需詳細資訊,請參閱 選單。
類別樣式
類別樣式會定義窗口類別的其他元素。 您可以使用位元 OR(|)運算子來結合兩個或多個樣式。 若要將樣式指派給窗口類別,請將樣式指派給WNDCLASSEX結構的樣式成員。 如需類別樣式的清單,請參閱 視窗類別樣式。
類別和裝置內容
裝置內容是一組特殊的值,應用程式使用這些值來在其視窗的用戶區域中繪圖。 系統需要顯示器上每個視窗的裝置內容,但允許系統儲存和處理該裝置內容的方式有一些彈性。
如果未明確指定裝置內容樣式,系統會假設每個視窗都會使用從系統維護的內容集區擷取的裝置內容。 在這種情況下,每個視窗在繪製之前必須先擷取並初始化裝置內容,繪製後再釋放裝置內容。
為了避免每次需要在視窗內繪製時擷取裝置內容,應用程式可以為視窗類別指定 CS_OWNDC 樣式。 此類別樣式會指示系統建立私人裝置內容,也就是為類別中的每個視窗配置唯一的裝置內容。 應用程式只需要擷取內容一次,然後用於所有後續繪製。
擴展類別記憶體
系統會在內部維護系統中每個視窗類別的 WNDCLASSEX 結構。 當應用程式註冊視窗類別時,它可以指示系統配置並附加一些額外的記憶體位元組到 WNDCLASSEX 結構的結尾。 此記憶體稱為 額外的類別記憶體 ,且由屬於 類別的所有窗口共用。 使用額外的類別記憶體來儲存與 類別相關的任何資訊。
由於從系統本機堆積配置額外的記憶體,因此應用程式應該謹慎使用額外的類別記憶體。 如果要求的額外類別記憶體數量大於 40 個字節, 則 RegisterClassEx 函式會失敗。 如果應用程式需要超過 40 個字節,它應該配置自己的記憶體,並將記憶體指標儲存在額外的類別記憶體中。
SetClassWord 和 SetClassLong 函式會將值複製到額外的類別記憶體。 若要從額外的類別記憶體擷取值,請使用 GetClassWord 和 GetClassLong 函式。 WNDCLASSEX 結構的 cbClsExtra 成員會指定要配置的額外類別記憶體數量。 不使用額外類別記憶體的應用程式必須將 cbClsExtra 成員初始化為零。
額外的視窗記憶體
系統會維護每個視窗的內部數據結構。 註冊窗口類別時,應用程式可以指定一些額外的記憶體位元組,稱為 額外的視窗記憶體。 建立 類別的視窗時,系統會配置並附加指定數量的額外視窗記憶體至窗口結構的結尾。 應用程式可以使用此記憶體來儲存視窗特定數據。
由於會從系統本機堆積配置額外的記憶體,因此應用程式應該謹慎使用額外的視窗記憶體。 如果要求的額外視窗記憶體數量大於 40 個字節, 則 RegisterClassEx 函式會失敗。 如果應用程式需要 40 個以上的位元組,它應該配置自己的記憶體,並將記憶體的指標儲存在額外的視窗記憶體中。
SetWindowLong 函式會將值複製到額外的記憶體。 GetWindowLong 函式會從額外的記憶體擷取值。 WNDCLASSEX 結構的 cbWndExtra 成員會指定要配置的額外視窗記憶體數量。 不使用記憶體的應用程式必須將 cbWndExtra 初始化為零。