視窗

Window
圖形用戶界面GNOME上的兩個-{zh-hant:程式; zh-hans:程序}-窗口，其中一個窗口與另一個的一部分重疊。（圖為Ubuntu作業系統）
視窗是在使用者界面上的一個可見的範圍。視窗一般都是長方形的。它包含著各種控件，是用作輸入和輸出的界面。雖然視窗一般用於圖形使用者界面，但又有時用於命令行界面的。而一個使用視窗為主要用戶界面的系統則稱為視窗系統。這個想法源於施樂帕洛阿爾託研究中心。
在視窗系統上，視窗一般被畫成一些二維的物件，被安置在桌面之上。大多數的視窗都可以隨使用者的意願而更改大小、移動、隱藏、回復和關閉。當兩個視窗重疊的時候，就如日常生活中一樣，其中一個要位於另一個之上，而下方的則會有些地方被上方個視窗所遮蓋。而視窗系統中管理這個操作的部份，則叫做視窗管理員。
視窗是數種圖形使用者界面中的一種重要的widget。當中有VMS的DEC Windows、GNU/Unix-like系統的X Window系統、Microsoft Windows和Open Windows。

**歷史**
GTK+的文件“另存為……”對話框
這個概念是由SRI International(由 道格拉斯·恩格爾巴特 領導）開發實現的。 他們最早的系統支持多窗口，但沒明顯的方法顯示它們的分界線（比如窗口邊框、標題欄等）。 
研究在 Xerox 公司的 Palo Alto 研究中心 / PARC （由 艾倫·凱領導）中繼續。他們使用了重疊的窗口。
在1980年代，PARC 提出術語"WIMP"，分別代表了窗口、圖標、菜單、指針。
那時Apple與PARC進行了簡單的合作。Apple 根據 PARC 的界面開發了自己的新界面。它首次應用是在 Apple的 Lisa上，此後用於 Macintosh 電腦。與此同時，Microsoft在為"Mac"開發辦公軟件。之後他們在 Apple 系統的基礎上開發了自己的窗口系統。

**子類化與超類化**
窗口子類化是對一個窗口實例通過GetWindowLong和SetWindowLong將窗口過程地址修改為一個新函數地址，新的窗口過程函數處理那些感興趣的消息，將其它消息傳遞給原窗口過程。即實例子類化。在某控件已經創建的情況下，為了獲得窗口消息，必須子類化它。子類化不需要創建一個完整的新窗口類，只需要攔截單個窗口。
窗口超類化是一個已存在的窗口類（WNDCLASS或WNDCLASSEX），改變窗口類的特徵。僅影響窗口類新創建的窗口實例，不會影響窗口過程地址修改前已經創建的窗口。超類化只能改變用戶自創的窗口的特性，不能用於系統創建的窗口(如對話框上的標準按鈕）。超類話過程示例如下：
WNDCLASSEX wc;
wc.cbsize=sizeof(wc);
GetClassInfoEx(hinst,"XXXX",&wc); //hinst---定義窗口類XXXX的模塊的句柄，如為系統定義的窗口類(如: Edit,Button)則hinst=NULL
wc.lpszClassName="YYYY";//必須改變窗口類的名字
wc.hbrBackground=CreateSolidBrush(RGB(0,0,0));
wc.lpfnWndProc=NewWndProc;//改變窗口過程
RegisterClassEx(&wc);

**MFC的子類化與超類化**
MFC的CWindowImplBaseT::SubclassWindow用於動態地把一個窗口實例綁定到當前的CWnd對象，消息循環首先調用CWnd對象的窗口過程，傳遞給基類的消息將被默認的窗口過程處理。CWnd::SubclassDlgItem用於把對話框的一個控件子類化綁定到一個基於CWnd的類對象。CWindowImplBaseT::UnsubclassWindow恢復一個被子類化的窗口。
模板類CWindowImplBaseT提供一個數據成員WNDPROC m_pfnSuperWindowProc並且初始化為 ::DefWindowProc.然而在窗口超類化處理時它存儲了已註冊窗口類的窗口過程，在窗口子類化時它保存窗口實例句柄原有的窗口過程。
CWnd::CreateEx在創建窗口前調用SetWindowsHookEx函數安裝了一個鉤子函數_AfxCbtFilterHook。窗口剛創建好，鉤子函數_AfxCbtFilterHook就被調用。_AfxCbtFilterHook調用SetWindowLong將窗口過程替換為AfxWndProcBase，並將SetWindowLong返回的原窗口地址保存到成員變量oldWndProc。可見，通過CWnd::CreateEx創建的所有窗口都會被子類化。在DefWindowProc函數中，消息會傳給子類化時保存的原窗口地址oldWndProc。
ATL提供DECLARE_WND_SUPERCLASS來支持超類化。

**Windows窗口類型**
使用Windows API函數CreateWindowEx，可以指定創建的窗口的類型：
*層疊窗口 Overlapped Window：一種頂層（top-level)窗口，作為應用程序的主窗口。有標題條、邊界與客戶區，可以有菜單、最大最小化按鈕、滾動條、可改變窗體尺寸的邊界（sizing border）。使用CreateWindowEx函數指定WS_OVERLAPPED 或 WS_OVERLAPPEDWINDOW風格。
*彈出窗口 Pop-up Window：另一種頂層窗口，用於對話框、MessageBox，以及其他出現在應用程序主窗口之外的臨時窗口。標題條可選，除此以外與層疊窗口相同。使用CreateWindowEx函數，指定WS_POPUP風格。如果需要標題條，指出WS_CAPTION風格。使用WS_POPUPWINDOW風格有border與window菜單。彈出窗口可以擁有其他頂層窗口；頂層窗口也可以擁有彈出窗口；前述二者可以同時成立。彈出窗口總是有WS_CLIPSIBLINGS風格。彈出窗口的尺寸或定位參數不能使用CW_USEDEFAULT，否則尺寸、定位被設為0。
*頂層窗口 top-level window：就是“非Child window”。其parent恆定是Desktop。總是有WS_CLIPSIBLINGS風格。
*子窗口 Child Window：出現在父窗口（parent window）的客戶區中，超出的部分不可見。子窗口一定有Parent，一定沒有Owner。非子窗口的Parent一定是桌面，它們不一定有Owner。典型用於把父窗口的客戶區分割成一些子區域。使用CreateWindowEx函數指定WS_CHILD風格。父窗口可以是Overlapped、Pop-up、其他子窗口。子窗口的唯一必要特性是有客戶區；不能有菜單；其他特性可選。定位子窗口時總是用父窗口客戶區左上角座標系。Destroyed、Hidden、Moved、Shown等窗口操作與父窗口一致。父窗口如果指定了WS_CLIPCHILDREN風格，則父窗口不能在子窗口上繪製；否則重繪父窗口會覆蓋子窗口的內容。兄弟子窗口（sibling window）可以在彼此重疊的客戶區上繪製，除非指定了WS_CLIPSIBLINGS風格，則不論子窗口之間z-order，與其他子窗口重疊區域不被繪製。不能使用CW_USEDEFAULT，否則將沒有尺寸、沒有定位位置。子窗口的輸入消息直接發給子窗口，並不傳遞給父窗口。但子窗口被函數EnableWindow 禁止時，系統把子窗口的輸入消息發給父窗口。在子窗口上點擊鼠標，子窗口收到WM_LMOUSEBUTTONDOWN消息；如果主窗口沒有鍵盤輸入焦點（focus），則主窗口獲得輸入焦點（WM_SETFOCUS）；如果兄弟子窗口有輸入焦點，則當前子窗口獲得輸入焦點。子窗口有獨一無二整數標識符。應用程序通過空間的整數標識符給它發消息。控件給父窗口發送notification消息時，用整數標識符來表示自身。 
*Layered Window：帶有WS_EX_LAYERED風格的窗口就是分層窗口，用於實現異形窗口和窗口整體透明。異形窗口要把控件的透明色和窗口後面的圖像進行融合，需要用UpdateLayeredWindow函數來繪製。窗口整體透明，需要用SetLayeredWindowAttribute函數設置透明度。調用UpdateLayeredWindow函數後，WM_PAINT消息將失效；更新窗口需要自行調用UpdateLayeredWindow函數。子窗口無法應用WS_EX_LAYERED風格。子窗口如果需要是異形或者整體透明，只能去掉WS_CHILD風格，作為popup窗口，然後MOVE到一定位置，在父窗口移動的時候跟隨移動。
*Message-Only Window，允許發送/接收Windows消息，不可見，沒有z-order，不能被枚舉，不接收廣播消息。CreateWindowEx函數的參數hWndParent為HWND_MESSAGE常量或者一個已存在的message-only窗口的句柄。對已經存在的窗口，通過函數SetParent的參數hWndNewParent為HWND_MESSAGE，把該窗體修改為message-only。為搜到message-only窗口，調用函數FindWindowEx指定參數hwndParent為HWND_MESSAGE。此外，函數FindWindowEx搜索message-only窗口與頂層層口，如果參數hwndParent與hwndChildAfter都為NULL。
*Owner/Owned Window：Overlapped Window/Pop-up Window可以被其他Overlapped Window/Pop-up Window擁有。子窗口一定沒有Owner。這種關係具有如下特性：在z-order上，Owned總是在Owner之上。當Owner被摧毀時，系統自動摧毀它的所有Owned。當Owner極小化時，Owned自動隱藏（沒有了WS_VISIBLE），不顯示在任務欄上；但Owned窗口自身擁有的窗口不會自動隱藏，即沒有傳遞性。Owner隱藏（SW_HIDE），不會影響其擁有的窗口。CreateWindowEx函數創建具有WS_OVERLAPPED或WS_POPUP風格的窗口時，通過參數hwndParent指定Owner。對話框與消息框默認都是Owned。通過GetWindow函數指出GW_OWNER標誌獲取窗口的Owner的句柄。
**Owner Window：Owner window不能是Child window。 Child window一定沒有Owner，因此Owner window是對popup和Overlapped而言。popup和Overlapped不一定有Owner。Owner為NULL的非Child窗口可以在任務欄上出現它們的按鈕。
**Owned Window
*top most window：具有“WS_EX_TOPMOST”屬性。總是顯示在其它非top most窗口的上面。同時是top most的窗口是“公平競爭”的關係。owner為top most，則其全部owned都變成了top most。child不能是top most窗口。
GetParent函數，對於子窗口返回其父窗口句柄；對於彈出窗口，返回擁有者窗口句柄；對於層疊窗口，返回0.
GetWindow函數，指出GW_OWNER參數，則可以返回彈出窗口或層疊窗口的擁有者窗口句柄。
FindWindowEx函數，用於找到指定父窗口、指定子窗口的下一個子窗口。

**Windows窗口類與窗口實例的額外存儲空間**
WNDCLASS中cbClsExtra和cbWndExtra，分別用於指定附加於窗口類與窗口實例的額外存儲空間字節大小，不能超過40字節。分別類似於C++類中的static變量與類似於C++類中的成員變量。
GetClassLong(hwnd,GCL_CBCLSEXTRA)獲取的是類附加空間的大小，即cbClsExtra的值。GetClassLong(hwnd,GCL_CBWNDEXTRA)獲取的是窗口附加空間的大小，即cbWndExtra的值。
使用方法：- 訪問類附加空間的數據GetClassLong(hwnd,index)，index表示訪問附加空間以0開始的索引。- 設置類附加空間的數據SetClassLong(hwnd,index,value)，index表示訪問附加空間以0開始的索引，value表示要設置的值。- 訪問窗口附加空間的數據GetWindowLong(hwnd,index)，index表示訪問附加空間以0開始的索引。- 設置窗口附加空間的數據SetWindowLong(hwnd,index,value)，index表示訪問附加空間以0開始的索引，value表示要設置的值。
其它相關Windows API有：GetWindowWord、GetWindowLongPtr、SetWindowWord、SetWindowLongPtr。

**窗口管理器**
視窗系統中管理視窗的部分被成為"窗口管理器"
常見的窗口系統：- X Window系統 - Microsoft Windows

**參考文獻==