Windows系統中owner owned 關係和 parent child關係

吳尼瑪發表於2017-12-19

一、概念

  • 在windows系統中,每個視窗物件都對應有一個資料結構,形成一個list連結串列。系統的視窗管理器通過這個list來獲取視窗資訊和管理每個視窗。這個資料結構中有四個資料用來構建list,即child、sibling、parent、owner四個域。

  • 所以我們可以看到,視窗之間的關係有兩種:owner-owned 關係parent-child關係。前者稱之為擁有/被擁有關係,後者稱之為父/子關係。

  • 視窗之間的關係,決定了視窗的外在表現。比如顯示、銷燬等。

  • 如果一個視窗資料的owner域(m_hWndOwner)非NULL,則它和該視窗建立了owner-owned 關係,擁有關係決定了:

    • 被擁有的視窗永遠顯示在擁有它的那個視窗的前面;
    • 當所有者視窗最小化的時候,它所擁有的視窗都會被隱藏;
    • 當所有者視窗被銷燬的時候,它所擁有的視窗都會被銷燬。
  • 注意!最小化所有者視窗並不會影響它所擁有的視窗的可見狀態

  • 如果一個視窗的parent域(m_hParent)非NULL,則它和該視窗之間就建立了parent-child關係。父子關係決定了:

    • 視窗在螢幕上面的顯示位置。父視窗提供了用來定位子視窗的座標系統,一個子視窗只能顯示在它的父視窗的客戶區中,之外的部分將被裁減。
    • 當父視窗被最小化時,它的所有子視窗也都被隱藏。
    • 當父視窗被銷燬的時候,它所擁有的子視窗也都會被銷燬。
  • 注意!最小化父視窗不會影響子視窗的可見狀態,子視窗會隨著父視窗被最小化,但是它的WS_VISIBLE屬性不會變

  • Windows系統使用兩種關係是為了更加靈活的管理視窗。父視窗會限制子視窗顯示區域,而擁有者視窗則不會限制被擁有視窗顯示區域。這是他們最大的區別。

  • 另外,視窗之間訊息的傳遞也和視窗關係有關,通常,一個視窗會把自己的通知訊息傳送給它的父視窗,但不全是這樣,這樣視窗之間的訊息傳遞也可以靈活控制。

二、視窗型別的說明和限制

  • 控制檯視窗(desktop window)。這是系統最早建立的視窗。可以認為它是所有WS_OVERLAPPED型別視窗的所有者和父視窗。當系統初始化的時候,它首先建立控制檯視窗,大小覆蓋整個螢幕。所有其它視窗都在這個控制檯視窗上面顯示。視窗管理器所用的視窗list中第一個就是這個控制檯。它的下一層視窗叫做頂級視窗(top-level),頂級視窗是指所有非child視窗、沒有父視窗,或者父視窗是desktop的視窗,它們沒有WS_CHILD屬性。

  • WS_OVERLAPPED型別的視窗可以顯示在螢幕的任何地方。它的所有者視窗是控制檯。Overlapped的視窗屬於頂級視窗,一般作為應用程式的主視窗。不論是否給出了WS_CAPTION、WS_BORDER屬性,這類視窗建立後都有標題欄和邊框。所有overlapped視窗都有WS_CLIPSIBLINGS屬性。當系統關閉的時候,它將銷燬所有overlapped型別的視窗。

  • WS_POPUP型別的視窗可以顯示在螢幕任何地方,一般沒有父視窗,但是如果明確呼叫SetParent,這類視窗也可以有父視窗。和WS_OVERLAPPED型別的視窗類似,WS_POPUP型別的視窗的所有者是在CreateWindow或者CreateWindowEx函式中通過設定hWndParent引數給定的,如果hWndParent不是子視窗,則該視窗就成為這個新的彈出式視窗的owner,否則,系統從hWndParent的父視窗向上找,直到找到第一個非子視窗,把它作為該彈出視窗的owner。在這種情況下,引數hwndParent被儲存在新建視窗的parent域中,而它的所有者視窗控制程式碼則儲存在owner域中。當owner視窗銷燬的時候,系統自動銷燬這個彈出視窗。

  • WS_POPUP型別的視窗也屬於頂級視窗,它和WS_OVERLAPPED視窗的主要區別是WS_POPUP視窗不需要有標題欄,也不必有邊框。

  • 所有者視窗只能是WS_OVERLAPPED或者WS_POPUP型別的視窗,子視窗不能是所有者視窗,並且所有者視窗也可以被擁有。

  • 預設情況下,對話方塊和訊息框屬於owned視窗,除非在建立它們的時候明確給出了WS_CHILD屬性,(比如對話方塊中嵌入對話方塊的情形)否則由系統負責給它們指定owner視窗。需要注意的是,一旦建立了owned型別的視窗,就無法再改變其所有關係,因為Win32沒有沒有提供改變視窗所有者的方法。而且在Win32中,由於有多執行緒的存在,所以要注意保證parent-child視窗或者owner-owned視窗要同屬於一個執行緒。

  • WS_CHILD型別的視窗的父視窗就是它的所有者視窗。一個子視窗的父視窗也是在CreateWindow函式中用hWndParent引數指定的。子視窗只能在父視窗的客戶區中顯示,並隨父視窗一起銷燬。**子視窗必須有一個父視窗,這是它和WS_OVERLAPPED以及WS_POPUP視窗之間的主要區別。**父視窗可以是頂級視窗,也可以是其它子視窗。

三、幾個相關函式的說明

  • 獲取所有者視窗**GetWindow(hWnd, GW_OWNER)**永遠返回視窗的所有者(owner)。對於子視窗,函式返回NULL,因為Windows系統沒有維護子視窗的所有者資訊。

  • 獲取/設定父視窗,Win32 API給出了函式GetParent和SetParent。對於SetParent,msdn裡面說明了父子視窗必須是同一個程式的。但是由於視窗控制程式碼是系統全域性唯一的,不屬於同一個程式的情況下,也可以成功呼叫,但是後果未知。GetParent的返回值比較複雜,對於WS_OVERLAPPED型別的視窗,它返回0,對於WS_CHILD型別,它返回其父視窗,對於WS_POPUP型別,它返回其所有者視窗。

  • **GetWindowWord(hWnd, GWW_HWNDPARENT)**返回一個視窗的父視窗,如果沒有,則返回其所有者。

  • **ShowOwnedPopups(hWnd,fShow);**函式可以將owner視窗所有擁有的視窗一起顯示或者隱藏。

相關文章