简体   繁体   English

使 Qt 顶级窗口由单个进程中的其他非 Qt 窗口“拥有”

[英]Make Qt top-level window “owned” by other non-Qt window within a single process

Is it doable on Windows through Qt API?通过 Qt API 在 Windows 上可行吗? I need such feature/behavior because I would like to integrate my Qt code as a plugin into a 3rd party application , and I want to do it as seamless as possible.我需要这样的功能/行为,因为我想将我的 Qt 代码作为插件集成到 3rd 方应用程序中,并且我想尽可能无缝地做到这一点。 For instance, I don't want to have another item on the taskbar representing my Qt window.例如,我不想在任务栏上有另一个项目代表我的 Qt 窗口。 I'm talking about a single-process application .我说的是单进程应用程序

I have found one way using WinAPI and HWNDs:我找到了一种使用 WinAPI 和 HWND 的方法:

// The Qt frame is "alien". We need its HWND so transform it (and all its ancestors,
// which is probably fine for me) to a "native" widget first. 
Q_ASSERT(!frame->testAttribute(Qt::WA_NativeWindow));
frame->setAttribute(Qt::WA_NativeWindow);
Q_ASSERT(frame->testAttribute(Qt::WA_NativeWindow));

// Get the HWND.
QWindow* frameHandle = frame->windowHandle();
Q_ASSERT(frameHandle);
HWND frameHwnd = HWND(frameHandle->winId());
Q_ASSERT(frameHwnd);

// And use it to set the frame's "owner" (not "parent" because WS_CHILD bit is not set).
Q_ASSERT((GetWindowLongPtr(frameHwnd, GWL_STYLE) & WS_CHILD) == 0);
SetWindowLongPtr(frameHwnd, GWLP_HWNDPARENT, LONG_PTR(getOtherNonQtWindowHwnd()));

This seems to work as expected if it is done after showing the frame.如果显示框架完成这似乎按预期工作。 But such solution is kind of strange and I'm afraid it is even dangerous.但这样的解决方案有点奇怪,恐怕甚至是危险的。 Furthermore, may be the setup could be overriden by Qt itself any time later.此外,该设置可能会在以后任何时间被 Qt 本身覆盖。

As can be seen in the snippet above, I'm able to retrieve HWND of the 3rd party application's main window.从上面的代码片段中可以看出,我能够检索 3rd 方应用程序主窗口的 HWND。 So generally from WinAPI perspective it should be doable.所以一般从 WinAPI 的角度来看,它应该是可行的。 Please, is there some Qt way of doing this?请问,有没有一些Qt方法可以做到这一点? Or at least a better solution?或者至少是更好的解决方案?

Another way to do it is by exposing your code as an ActiveX control, using the ActiveQt module.另一种方法是使用ActiveQt模块将代码公开为 ActiveX 控件。 That way, Qt will handle all the nitty-gritty for you.这样,Qt 将为您处理所有细节。

Your solution is fine as well.您的解决方案也很好。 It is about the most "Qt" way of doing it you can get.这是关于您可以获得的最“Qt”的方式。 With two nitpicks:有两个挑剔:

  1. Since Qt is responsible for the lifetime of the child window, you must not set the owner.由于 Qt 负责子窗口的生命周期,因此您一定不要设置所有者。 Set the parent instead.改为设置父级。 It's on you to set the WS_CHILD style in that case - after all, you're about to make the window a child.在这种情况下,由您来设置WS_CHILD样式 - 毕竟,您将要使窗口成为子窗口。

  2. The frame should not have any ancestor windows to begin with. frame不应该有任何祖先窗口开始。 You must ensure that the frame has its own window.您必须确保框架有自己的窗口。

Thus:因此:

bool reparentToNativeWindow(QWidget * widget, HWND newParent) {
  widget->setWindowFlags(Qt::Window);
  if (!widget->testAttribute(Qt::WA_NativeWindow));
    widget->setAttribute(Qt::WA_NativeWindow);  
  auto childHandle = widget->windowHandle();
  if (!childHandle)
    return false;
  auto child = HWND(widgetHandle->winId());
  if (!child)
    return false;

  auto style = GetWindowLongPtr(child, GWL_STYLE);
  if (!style)
    return false;
  if ((style & WS_CHILD) == 0) {
    Q_ASSERT(style & WS_POPUP);
    style &= ~WS_POPUP;
    style |= WS_CHILD;
    if (SetWindowLongPtr(child, GWL_STYLE, style) == 0)
      return false;  
  }
  SetParent(child, newParent);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM