Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-57159

Window with WS_CHILD style can become an owner of an overlapped or pop-up window when reparented with SetWindowLongPtr(GWL_HWNDPARENT)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P4: Low
    • 5.9.0 Beta 2
    • 5.4.1, 5.7.0
    • QPA: Windows
    • None
    • 79352528a1726b4551ea4d9285dd2394dd0d43da (qtbase/5.8 24.1.2017, 5.8.1)

    Description

      According to https://msdn.microsoft.com/en-us/library/ms632599(v=VS.85).aspx
      Only an overlapped or pop-up window can be an owner window; a child window cannot be an owner window. An application creates an owned window by specifying the owner's window handle as the hwndParent parameter of CreateWindowEx when it creates a window with the WS_OVERLAPPED or WS_POPUP style. The hwndParent parameter must identify an overlapped or pop-up window. If hwndParent identifies a child window, the system assigns ownership to the top-level parent window of the child window. After creating an owned window, an application cannot transfer ownership of the window to another window.

      This is how it works indeed when CreateWindowEx() is passed a window with WS_CHILD flag in the parent parameter.

      However later with the fix for QTBUG-30707 a new function updateTransientParent() has been introduced that uses (only partially documented) SetWindowLongPtr(GWL_HWNDPARENT) interface to change owner of created windows. It turns out that unlike CreateWindowEx(), SetWindowLongPtr(GWL_HWNDPARENT) does not respect the rule cited above. As a result, a window with WS_CHILD flag can end up to be the owner of an overlapped or pop-up window.

      This breaks QWinWidget, because it uses WS_CHILD style internally AND it is set as a parent for Qt-style windows.

      For example, say we have a HWND nativeWnd handle, a QWinWidget* winWidget = new QWinWidget(nativeWnd) and a Qt-style window QWidget* qtWidget = new QWidget(winWidget).

      Before QTBUG-30707 we had the following ownership hierarchy:
      nativeWnd > qtWidget>winId()
      this is becase qtWidget->winId() has WS_CHILD and CreateWindowEx() assigns first non-child window as actual owner.

      After QTBUG-30707 we now have:
      nativeWnd > winWidget>winId() > qtWidget>winId()
      despite the fact that winWidget->winId() has WS_CHILD set. This is plain wrong and leads to some nasty modality, focus and z-order issues.

      Attached is the patch I came up with that fixes the problem for me. The idea is to traverse up the hierarchy and find first non-child window before calling SetWindowLongPtr(GWL_HWNDPARENT).

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              kleint Friedemann Kleint
              skoulik Sergei Kulik
              Votes:
              1 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes