GUI freeze when using Windows::SetParent on QApplication

1.9k views Asked by At

It is about the communication two Qt programs under Windows. One program, let's call him Client, The other program, called Server. The situation: I want to place the Client inside a QWidget of the server. Windows already supplies some nice methods for removing the decoration (border,title bar etc..) and change the parent of a window so repainting, resizing, window activation is all taken care by Windows. When I start my Client using a QProcess I wait for it to be launched, so that there is a window I can talk to. Then I remove the decoration and set the QWidget of the Server as parent. All done with this code:

winHandle = ::FindWindowA(NULL, "My Client");//get clients window id
if(winHandle != NULL)
{
   ::ShowWindow(winHandle, SW_HIDE);

    // Remove the window border
    LONG lStyle = GetWindowLong(winHandle, GWL_STYLE);
    lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU); 
    ::SetWindowLong(winHandle, GWL_STYLE, lStyle);

    ::SetParent(winHandle, (HWND)(ui->widget->effectiveWinId()));//set the server's widget to be the parent of the client win
    ::SetWindowPos(winHandle, HWND_TOP, ui->widget->pos().x(), ui->widget->pos().y(), 0, 0 , SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_ASYNCWINDOWPOS);

     ::UpdateWindow(winHandle);
     ::ShowWindow(winHandle, SW_SHOW);
 }

This all works perfectly, my Client is nicely placed on top of my tab and all the repainting etc. is working perfect. BUT, The problem I'm facing is that sometimes (not always!) the servers' buttons become not responding. I noticed that when such a case happens, the buttons don't respond as long as they are located in the middle of the screen. but, the weirdest thing is that if i move the whole window so the buttons are located close to the edge of the screen - they work! if i move it back to the center - they stop working again. any idea?? someone?

I tried also the following code:

        QWindow * window = QWindow::fromWinId((WId) winHandle);
        QWidget * widget = QWidget::createWindowContainer(window);
        widget->setParent( ui->widget);
        QVBoxLayout *layout = new QVBoxLayout();
        layout->addWidget(widget);     
        ui->widget->setLayout(layout);

with this solution, the GUI didn't freeze but the keyboard doesn't work now in the client's window - in the internal window. for example - if the internal window is notepad - i cannot type inside it but i can use the mouse. any idea what can be done?

2

There are 2 answers

6
Kuba hasn't forgotten Monica On BEST ANSWER

You may try to do this by obtaining a QWindow for the native window, and then creating a QWidget wrapper for it. This needs at least Qt 5.2.

For example:

HWND winHandle = ::FindWindowA(NULL, "My Client");
if (! winHandle) return;
QWindow * window = QWindow::fromWinId((WId)winHandle);
if (! window) return;
QWidget * widget = QWidget::createWindowContainer(window);
if (! widget) return;
// At this point you can use Qt to change window flags, reparent/embed, etc.

You need to ask the winHandle about its minimum and maximum size and pass those onto the widget. You also need to allow the widget to be focusable if you intend to interact with it.

0
Adrian McCarthy On

Clear the WS_POPUP style of the window to be reparented and set the WS_CHILD bit instead. Otherwise, I think SetParent actually sets up an owner/owned relationship rather than a parent/child one.

When you reposition the child, it should be in the parent's client coordinates. The fact that you're using screen coordinates is a good hint that you don't have a proper parent/child relationship. (It's also consistent with the UI working near the top-left of the screen where screen coordinates will be very close to client coordinates.)

Despite the negative comments, it is possible to make everything work with cross-process parent/child windows. That said, it can be challenging to get it right.