通过小部件获取 top-level window

Getting top-level window by widget

我正在挂钩 Windows 上 Qt5 应用程序的 QPainter::drawText() 函数。 我的目标是识别绘制文本的 top-level-window 的本机句柄。首先,我正在获取关联的小部件。

QWidget *widget = static_cast<QWidget *>(painter->device());

所以应该可以找到对应的top-levelwindow/widget。 但这比我想象的要难。这是我到目前为止尝试过的:

while (widget->parentWidget())
    widget = widget->parentWidget();

HWND hwnd = (HWND) widget->winId();

没有成功。 top-parent 永远不是想要的 window.

QApplication::topLevelWidgets()

向我展示了一个 window 包含多个 top-level 小部件(包括我正在寻找的小部件)。

我也试过了QApplication::topLevelAt(widget->mapToGlobal(QPoint()))

在某些情况下这确实有效,但并不可靠。 根据文本和 window 位置,我得到 AccessViolationException, 所以这不是一个选择。

通过测试widget->testAttribute(Qt::WA_NativeWindow) 我发现大多数小部件都是 non-native Alien Widgets.

我就是这样得到(我所说的)top-level window.

WinAPI.EnumChildWindows(
    WinAPI.GetDesktopWindow(),
    new EnumWindowsProc(this.EnumWindowsCallback), 0);

然后我查看 window 标题以找到我感兴趣的句柄。

我无法找到任何 (low-level) 小部件与拥有 window 标题的 (top-level) 小部件之间的关系。

对于作为顶层的QWidgetwindow,call QWidget::window().

对于具有本机句柄的最近父级,call QWidget::nativeParentWidget()

调用 winId() 强制小部件获取本机 window 句柄(如果它没有 ),这不是您的目标。顶级 window 将始终具有本机 ID,因此 (HWND)window()->winId() 没问题。请注意,这 通常 calling QWidget::effectiveWinId() 相同。

完成!我找到了解决问题的方法。

每个 windows 都有自己的线程。

int threadId = WinApi.GetWindowThreadProcessId(wndHandle, IntPtr.Zero)

有了它我用EnumThreadWindows 获取此线程创建的所有 window-句柄的列表。

最后我检查了widget->effectiveWinId()是否在列表中。

所以我可以将每个小部件映射到其对应的 window!