原文連結:undefined!
相信在Windows 下面程式設計的很多兄弟們都不是很清楚Windows 中視窗的層次關係是怎麼樣的,這個東西很久已經研究過一下,後來又忘記了,今天又一次遇到了這個問題,所以便整理一下。下面就說說Windows 中桌面(Desktop)以及頂層視窗,以及子視窗之間的關係。
在Windows 的圖形介面下,最基本顯示資訊的元素就是視窗,每一個Windows 視窗都管理著自己與其他視窗之間的關係和自身的一些資訊,如:是否可見,視窗的所有者,視窗的父/子關係等等資訊,當視窗建立、銷燬、顯示的時候,就會用到這些資訊。
在每一個視窗例項中,有四個元素被視窗管理器用來建立視窗管理連結串列。
- Child : 指向視窗子視窗的控制代碼
- Parent:指向視窗父視窗的控制代碼
- Owner:指向視窗所有者的控制代碼
- Next: 指向下一個同屬視窗的控制代碼
眾所周知當Windows初始化的時候,它建立桌面這個視窗,桌面覆蓋著整個視窗,視窗管理器用這個視窗作為視窗連結串列中第一個元素。因此桌面在視窗的層次關係中在最上層。
在視窗層次關係中,桌面視窗下一層視窗叫做頂層視窗,頂層視窗就是那些不是子視窗的視窗,頂層視窗不能夠有WS_CHILD屬性。視窗管理器是如何把桌面視窗和頂層視窗聯絡起來的呢?視窗管理器把頂層視窗都組織到一個連結串列中,而這個連結串列的頭儲存的就是桌面視窗的子視窗控制代碼,每一個子視窗通過Next就可以找到連結串列中下一個視窗了。這個連結串列被稱為子視窗連結串列,在同一個子視窗連結串列中的視窗是互為同屬視窗,所有頂層視窗都是同屬視窗。視窗在子視窗連結串列中的次序,也表明了視窗距離距離桌面視窗的距離。頂層視窗所形成的子視窗連結串列構成了一個Z 軸,視窗管理器就是根據Z序列來覺得視窗的哪一部分是顯示的,哪一部分是被遮蓋的。
所有頂層視窗的父視窗都是指向桌面視窗的,這樣一來頂層視窗就好像是桌面視窗的子視窗,所有頂層視窗構成的連結串列是桌面視窗的子視窗連結串列。當頂層視窗建立的時候,視窗管理器把頂層視窗放在Z軸的頂上,這樣使得整個視窗可見,視窗管理器把視窗插入到桌面視窗子視窗連結串列的前面。WS_EX_TOPMOST這個屬性控制著視窗管理器建立頂層視窗,視窗管理器把沒有WS_EX_TOPMOST屬性的視窗放在具有WS_EX_TOPMOST屬性的視窗的後面,這樣就使得具有WS_EX_TOPMOST屬性的視窗一直顯示在前面。
在頂層視窗之間還有另外一直關係,擁有或者屬於其他的頂層視窗,屬於其他視窗的視窗叫做歸屬視窗,擁有其他視窗叫做宿主視窗,在Z軸中,歸屬視窗一定在他的宿主視窗的前面,如果一個宿主視窗最小化,那麼歸屬他的視窗會隱藏掉,如果宿主視窗隱藏起來,歸屬他的視窗不會被隱藏掉。如果有三個視窗A、B、C,A擁有 B,B擁有 C,如果A最小化,那麼B會隱藏,但是 C還是可見的。怎麼才能夠在視窗之間建立所有關係呢?方法是在呼叫CreateWindow或者CreateWindowEx建立視窗的時候,指定hwndParent 引數。
桌面視窗是在視窗層次中的第一層,頂層視窗在視窗層次中的第二層,子視窗也就是那些建立的時候指定了WS_CHILD 屬性的窗口占據了視窗層次的其他層。視窗和子視窗之間的聯絡,就像桌面視窗和頂層視窗之間的關係一樣。
子視窗顯示在其父視窗的客戶區域,所有同一個視窗的子視窗同樣建立一個Z軸,這個和頂層視窗是類似的,頂層視窗也是顯示在其父視窗――桌面視窗的客戶區域。
16 位和32 位視窗系統的區別
視窗之間的父子關係、歸屬所有關係、以及根據 Z軸來顯示的這些規則在16位和32位視窗系統中都是相同的。這樣可以是在兩種視窗系統中高度的相容。兩種視窗系統的區別在於安全和多執行緒。
Windows NT 在原有的視窗層次關係中多增加了一層,每一個執行著Windows NT的系統中都有一個Windows 工作站物件,這個物件是安全物件的第一層,是所有使用者安全物件的繼承之源,每一個Windows工作站物件可以擁有一些桌面物件,每一個桌面都擁有上面描述的那樣的視窗關係。Windows Nt用了兩個桌面視窗物件,一個是用來處理登陸介面、遮蔽、鎖住工作站等,一個是我們登陸之後進來操作的視窗了。J通常使用者是不能夠建立和刪除桌面的,不過那是通常,實際上在Windows下面也可以實現類似 Linux 中的多個桌面的效果,每一個桌面都是一個獨立的世界。
兩種視窗系統還有兩位一個區別,在16 位視窗系統中不支援多執行緒,所以應用程式開發者在建立視窗的時候不必考慮執行緒的問題了。而在32位視窗系統中由於又支援了視窗的父子關係,歸屬與擁有關係,同一個視窗下面的所有執行緒都擁有相同的一個輸入佇列,應用程式開發者應該明白輸入佇列是共享的,在同一個時刻只能有一個執行緒處理訊息,其他的執行緒都在等待輸入佇列一直到GetMessage或者PeekMessage返回,而且必須注意的是父視窗和子視窗或者是歸屬與擁有視窗共用同一個執行緒。
在32 視窗系統中定義兩種新的視窗型別,前臺視窗和背景視窗,這兩種視窗沒有列到視窗的層次關係中,前臺視窗就是使用者當前操作的視窗,其他的所有視窗都是背景視窗。 32位視窗系統中支援兩個函式來處理前臺視窗SetForegroundWindow和GetForegroundWindow。
操作視窗列表
下面是視窗列表操作的一些函式:
Ø EnumChildWindows
使用這個函式得到一個視窗的所有子視窗,包括子視窗的子視窗。不過在列舉的過程中這個函式不能夠列出正在建立的或者銷燬的視窗。
Ø EnumThreadWindows
使用這個函式可以列出所有屬於這個執行緒的視窗。在這個函式呼叫之後建立的視窗是不能夠被列舉出來的。
Ø EnumWindows
使用這個函式列舉出所有頂層視窗,不能夠列舉出子視窗,要列出所有的頂層視窗,使用這個函式比GetWindow安全。使用GetWindow來列出所有的視窗,可能會導致程式無限迴圈,因為在呼叫GetWindow的過程中,可能一些視窗已經銷燬了。EnumWindows不能夠列舉出呼叫這個函式之後建立的頂層視窗。
Ø FindWindow
可以使用這個函式通過類名或者使用視窗的標題來找到頂層視窗,這個函式不能夠用來找子視窗,這個函式不區分引數的大小寫。這個函式在Z軸中尋找視窗,找到了之後,就會返回。
Ø GetDesktopWindow
得到桌面視窗控制代碼
Ø GetNextWindow
使用這個函式得到這個視窗的同屬視窗,在16 位視窗系統中GetNextWindow 和GetWindow是兩個不同的函式,在32位系統中這個函式是通過GetWindow來實現的。
Ø GetParent
如果一個視窗存在父視窗,那麼可以通過這個函式得到視窗的父視窗,如果視窗是頂層視窗,則返回其所有者視窗控制代碼。
Ø GetThreadDesktop
這個函式用來得到指定執行緒的所屬的桌面視窗控制代碼,在win95和 win98下面由於不支援多桌面,每次呼叫該函式都返回同一個值。
Ø GetTopWindow
可以用這個函式來得到給定視窗的第一個子視窗的控制代碼,如果傳遞給函式的引數是NULL的話,那麼這個函式將會返回最上面的頂層視窗。
Ø GetWindow
應用程式可以呼叫這個函式來在視窗列表中導航,這個函式有兩個引數,一個是視窗的控制代碼,另外是要得到的視窗控制代碼和這個視窗之間的關係。
· GW_HWNDNEXT:這個函式返回給定視窗的下一個同屬視窗
· GW_HWNDPREV:返回給定視窗的前一個同屬視窗
· GW_HWNDFIRST:返回給定視窗的第一個同屬視窗
· GW_HWNDLAST:返回給定視窗的最後一個同屬視窗
· GW_OWNER:返回給定視窗的所有者視窗控制代碼
· GW_CHILD:返回給定視窗的第一個子視窗控制代碼
Ø IsChild
這個函式有兩個引數,兩個視窗控制代碼,判斷兩個視窗是否存在父子關係
視窗的屬性
當應用程式呼叫CreateWindow建立視窗的時候,我們必須為視窗指定屬性,下面簡要的介紹一下視窗的屬性。
WS_OVERLAPPED
交迭屬性是頂層視窗的一種屬性,使用這種屬性建立的視窗,會被連結到桌面視窗的子視窗連結串列中,應用程式通常使用這種屬性的視窗作為應用程式的主視窗,具有交迭屬性的視窗通常具有有標題欄,即使是WS_CAPTION 這個屬性沒有指定。具有交迭屬性的視窗通常都是有邊框的,具有交迭屬性的視窗可以擁有自己的頂層視窗,也可以所屬其他的頂層視窗,所有的這類視窗都具有WS_CLIPSIBLINGS 屬性,即使是沒有給視窗指定這個屬性。
WS_POPUP
彈出屬性也是應用到頂層視窗的一種屬性,使用這種屬性建立的視窗會被連結到桌面視窗的子視窗連結串列中,應用程式通常為對話方塊視窗設定這個屬性,彈出屬性和交迭屬性的主要區別在於具有彈出屬性的視窗不是一定要有標題欄的,而具有交迭屬性的視窗則是一定要具有標題欄,具有彈出屬性的視窗可以沒有邊框。和具有交迭屬性的視窗一樣,具有彈出屬性的視窗可以有自己的頂層所屬視窗,也可以所屬其他的頂層視窗。所有具有彈出屬性的視窗必須具有WS_CLIPSIBINGS 屬性,即使是使用者沒有指定這個屬性。具有彈出屬性的視窗在建立的時候,它的大小和位置不能夠使用CW_USEDEFAULT 值。
WS_CHILD
子視窗必須具有這個屬性,子視窗只能夠出現在父視窗的客戶區域,這是子視窗和具有交迭屬性的視窗以及彈出屬性的視窗的主要區別,建立子視窗的時候,位置和大小不能夠使用CW_USEDEFAULT 這個值,否則是不能夠建立視窗的。
WS_CAPTION
當視窗被設定這個屬性的時候,視窗的最上頭會有標題欄,應用程式可以通過SetWindowText 這個函式來改變標題欄的標題,通常具有標題欄的視窗還具有最大、最小、關閉按鈕,和系統選單。如果一個視窗沒有標題欄,那麼Windows 是不會建立這些東西的,即使是使用者指定了這些屬性,系統選單是依賴標題欄視窗的存在而存在的,如果沒有標題欄那麼是一定不會有系統選單的存在的。具有標題欄的視窗通常具有單線的邊界具有可以改變視窗大小的屬性,通常具有標題欄的視窗是不能具有對話方塊的邊界屬性的,除非為視窗設定WS_EX_DLGMODALFRAME 屬性。
WS_MINIMIZEBOX
當為視窗設定這個屬性的時候,視窗的標題欄上會有一個最小化的按鈕,其實對於Windows 來實現這個屬性的時候,只是在標題欄上面放置了一個最小化的點陣圖,當用戶點選這個最小化點陣圖的時候,視窗最小化,如果最大化點陣圖最在,那麼最小化點陣圖被放置在最大化點陣圖的左邊。沒有這個屬性的視窗是不能夠最小化的。
WS_MAXIMIZEBOX
當為視窗設定這個屬性的時候,視窗的標題欄的右上會被放置一個最大化的點陣圖,如果視窗設定了這個屬性,使用者可以點選最大化的點陣圖或者是通過系統選單來實現視窗的最大化,沒有這個屬性的視窗是不能夠被最大化的。
WS_SYSMENU
如果為視窗指定這個屬性,那麼就會在視窗的左上角上放置系統選單點陣圖,系統選單為使用者提供了操作視窗的介面,通常系統選單會有下面這些系統命令:
- 恢復最小化的視窗
- 使用鍵盤移動視窗
- 使用鍵盤改變視窗的大小
- 最小化視窗
- 最大化視窗
- 關閉視窗
- 切換到其他的任務
如果一個視窗有系統選單,使用者可以通過點選系統選單圖示來呼叫系統選單,或者通過Alt+ 空格的快捷鍵調出系統選單,或者通過點選工作列上視窗的圖示來調出系統選單,如果一個視窗沒有系統選單,那麼使用者不能夠通過鍵盤來實現系統命令,除非應用程式自身提供了這樣的介面。系統選單對於最大化的視窗也是很有用處的,最大化的視窗覆蓋了整個螢幕,這樣的視窗不能夠被移動,除非恢復到不是最大化的狀態,如果這個最大化的視窗有了系統選單,則就不必一定恢復到非最大化的狀態才能夠移動。
WS_HSCROLL
如果視窗被指定了這個屬性,那麼視窗會有一個水平的滾動條,視窗是不會自動的滾動滾動條的,如果應用程式要支援滾動條,那麼必須自己處理WM_HSCROLL 訊息,這個屬性通常是在視窗建立的時候,被指定的。
WS_VSCROLL
如果視窗被指定了這個屬性,那麼視窗會有一個豎直的滾動條,視窗不會自動的滾動滾動條,應用程式必須自己處理WM_VSCROOL 訊息來處理滾動條滾動的訊息,這個屬性通常是在視窗被建立的時候指定的。
WS_BORDER
如果視窗被指定了這個屬性,那麼視窗會有一個單線的邊在視窗的周圍,如果沒有指定這個屬性,但是視窗具有標題欄,那麼視窗會自動的擁有這個屬性,如果視窗沒有這個屬性,擁有這個屬性的視窗不能夠通過鍵盤或者是滑鼠改變視窗的大小。
WS_DLGFRAME
如果視窗被指定了這個屬性,那麼視窗具有對話方塊的邊框,這個屬性通常是用在對話方塊視窗的,只能夠用在視窗沒有標題欄的情況下,如果一個不是對話方塊的視窗使用了這個視窗,那麼視窗必須被指定WS_EX_DLGMODALFRAME 屬性。使用這個屬性建立的視窗,不能夠通過鍵盤和滑鼠改變視窗的大小。
WS_THICKFRAME
當視窗被指定了這個屬性,那麼視窗會有一個可以改變大小的邊框,這種屬性通常用在程式的主視窗,具有這種屬性的視窗的大小可以通過鍵盤或者滑鼠來改變。
WS_CLIPCHILDREN
這個屬性用在具有子視窗的視窗,使用這個屬性,可以使Windows 把子視窗所佔的區域拷貝到父視窗,而不是甴父視窗直接的畫子視窗所屬的區域,如果視窗沒有指定這個屬性,那麼那麼父視窗會覆蓋子視窗的區域。在一些圖片顯示或者OpenGL 顯示的視窗中,指定這個屬性是很重要的。
WS_CLIPSIBLINGS
當視窗賦予這個屬性,視窗在自繪的時候,不會繪製到同屬的子視窗,所有具有交迭屬性和彈出屬性的視窗都具有這個屬性,所有的頂層視窗都具有這個屬性,這樣一來頂層視窗在自繪的時候,不會繪製在到其他的頂層視窗。
WS_VISIBLE
當視窗被設定這個屬性的時候,視窗是可見的,預設的情況下,應用程式必須自己呼叫ShowWindow 來顯示視窗。
WS_DISABLED
當視窗被設定這個屬性的時候,建立的視窗不能夠接受使用者的輸入,除非應用程式自身提供方法來輸入。這個屬性通常用在Windows 控制元件上面。
WS_CHILDWINDOWS
這個屬性同WS_CHILD。
WS_OVERLAPPEDWINDOWS
這個屬性同WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,這個屬性通常用在應用程式的主視窗。
WS_POPUPWINDOWS
這個屬性同WS_POPUP | WS_BORDER | WS_SYSMENU,儘管這個屬性中包含了WS_SYSMENU 屬性,如果視窗沒有 WS_CAPTION 屬性,那麼視窗也不會有系統選單。
WS_EX_DLGMODALFRAME
當視窗設定了這個屬性的時候,視窗具有對話方塊的邊框,這個屬性通常用在對話方塊視窗,不過任何視窗都可以使用這個屬性來獲得對話方塊的邊框。
WS_EX_NOPARENTNOTIFY
這個屬性是用在子視窗上的,當子視窗設定了這個屬性,Windows 不傳送WM_NOTIFY 訊息給子視窗的父視窗,預設情況下,Windows 會在子視窗建立或者銷燬的時候傳送WM_NOTIFY 訊息給子視窗的父視窗。
WS_EX_TOPMOST
這個屬性僅用在頂層視窗,對於子視窗設定這個屬性是被忽略的,如果視窗設定了這個屬性,那麼視窗會一直在其他視窗的上面。
WS_EX_ACCEPTFILES
視窗設定了這個屬性,那麼視窗可以接受拖放的物件。
WS_EX_TRANSPARENT
這個屬效能夠使視窗透明,設定了這個屬性的視窗的背景使可以被看到的,透明視窗對於滑鼠和鍵盤的訊息事件並不是透明的