在Qt开发中,窗口被外部(非Qt内部机制)强制销毁

警告信息

External WM_DESTROY received for QWidgetWindow(0x108b8cbdb10, name="xxxxx") , parent: QWindow(0x0) , transient parent: QWindow(0x0)

使用场景

代码结构如下:

  1. 自定义对话框类(CustomWaitDialog):
    静态函数getStaticDialog():返回静态对话框指针(如果为空则创建)
    静态函数waitShow(QWidget *parent):
    • 获取静态对话框指针
    • 设置父对象为传入的parent,并设置窗口标志(使用dialog->windowFlags())
    • 设置为模态(setModal(true))
    • 监听父对象的destroyed信号,当父对象被销毁时,将对话框的父对象设置为nullptr(使用setParent(nullptr))
    • 显示对话框
  2. 静态函数closeWait():关闭对话框
  3. 主窗口类(MainWindow):
    包含一个按钮,点击按钮时执行槽函数on_pushButton_clicked()
  4. 在槽函数on_pushButton_clicked()中:
    • 创建QDialog*tempWidget = new QDialog();
    • tempWidget->setAttribute(Qt::WA_DeleteOnClose);
    • 调用CustomWaitDialog::waitShow(tempWidget);
    • 模拟耗时(使用QTimer单次触发,在定时器结束后调用CustomWaitDialog::closeWait(),同时关闭tempWidget(因为设置了WA_DeleteOnClose,所以关闭即删除))

class CustomWaitDialog : public QDialog {Q_OBJECT
public:static void waitShow(QWidget* parent) {CustomWaitDialog* dialog = getStaticDialog();if (parent != nullptr){// 绑定父子关系并设置模态dialog->setParent(parent, dialog->windowFlags() | Qt::Dialog);dialog->setModal(true);// 监听父对象销毁事件QObject::connect(parent, &QWidget::destroyed, dialog, [dialog]() {dialog->setParent(nullptr);  // 解除父子关系}, Qt::UniqueConnection);}if (dialog->isVisible()) {dialog->activateWindow();return;}dialog->show();}static void closeWait() {if (auto dialog = getStaticDialog()) {dialog->close();}}private:// 禁止外部创建实例explicit CustomWaitDialog(QWidget* parent = nullptr): QDialog(parent) {// 初始化对话框内容QLabel* label = new QLabel("Please wait...", this);QVBoxLayout* layout = new QVBoxLayout(this);layout->addWidget(label);}~CustomWaitDialog() {}static CustomWaitDialog* getStaticDialog() {static QPointer<CustomWaitDialog> instance = nullptr;if (instance.isNull()) {instance = new CustomWaitDialog();instance->setWindowTitle("Processing...");instance->resize(150, 150);}return instance;}};void MainWindow::on_pushButton_clicked()
{// 1. 创建临时父窗口auto* tempContainer = new QDialog(this);tempContainer->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动删除// 2. 显示等待对话框tempContainer->setWindowTitle("等待中...");tempContainer->resize(this->size().width(), this->size().height());tempContainer->show();CustomWaitDialog::waitShow(tempContainer);// 3. 模拟耗时操作(实际中替换为真实操作)QTimer::singleShot(3000, this, [this, tempContainer]() {// 4. 关闭等待对话框CustomWaitDialog::closeWait();// 5. 关闭临时容器(自动触发WA_DeleteOnClose)tempContainer->close();// 6. 处理完成后续逻辑QMessageBox::information(this, "Complete", "Operation finished!");});
}

tempContainer父类析构时,setParent(nullptr)会输出警告

External WM_DESTROY received for QWidgetWindow(0x201e71f52b0, name="CustomWaitDialogClassWindow") , parent: QWindow(0x0) , transient parent: QWindow(0x0)

  • 导致第二次再重复使用此静态窗口时,不显示;
    问题现象

也就是setParent(nullptr)后再使用窗口不显示

解决方法一

  • 不监听父对象销毁事件和父类一起销毁,每次使用都新new
 /*移除:监听父对象销毁事件QObject::connect(parent, &QWidget::destroyed, dialog, [dialog]() {dialog->setParent(nullptr);  // 解除父子关系}, Qt::UniqueConnection);*/

解决方法二

  • 更改父对象,避免窗口被外部(非Qt内部机制)强制销毁 和每次使用都新new
// 方法二:更改父对象,避免窗口被外部(非Qt内部机制)强制销毁 和每次new
QObject::connect(parent, &QWidget::destroyed, dialog, [dialog]() {auto pActive = qApp->activeWindow();if (pActive == nullptr) return; 	// 如果没有活动窗口则和父类一起销毁dialog->setParent(pActive);		// 转移父窗口}, Qt::UniqueConnection);		

Windows系统强制销毁窗口机制分析与Qt解决方案

Windows窗口销毁机制

Windows系统强制销毁子窗口的核心机制源于窗口所有权模型(Window Ownership Model)。当父窗口被销毁时,系统会递归销毁所有子窗口,这是通过发送WM_DESTROY消息实现的。关键点:

  1. 系统级行为

    • 父窗口销毁时,Windows内核自动发送WM_DESTROY到所有子窗口
    • 这是Win32 API的底层行为(参考:微软官方文档)
    • Qt无法阻止此行为,只能通过父子关系管理规避
  2. Qt的封装限制

    External WM_DESTROY received for QWidgetWindow(0x108b8cbdb10)
    

    该警告表明Qt的窗口对象(QWidgetWindow)已被系统强制销毁,但Qt内部未同步此状态,导致Qt窗口对象处于"僵尸状态"。

问题根源分析

在代码中:

QObject::connect(parent, &QWidget::destroyed, dialog, [dialog]() {dialog->setParent(nullptr);  // 在父对象销毁后解除父子关系
});

此处存在时序问题

  1. 父窗口(tempContainer)开始销毁
  2. Windows系统自动发送WM_DESTROY给子窗口(CustomWaitDialog)
  3. Qt收到系统消息,标记窗口为已销毁状态
  4. 随后destroyed信号触发,执行setParent(nullptr)
  5. 此时Qt窗口对象已被系统销毁,但Qt尝试修改其父子关系,导致状态不一致

解决方案对比

方案一:每次创建新实例(简单可靠)

✅ 优点:彻底避免僵尸窗口问题
❌ 缺点:频繁创建/销毁带来轻微性能开销

方案二:动态重设父窗口(需谨慎处理)

QObject::connect(parent, &QWidget::destroyed, dialog, [dialog]() {auto pActive = qApp->activeWindow();if (pActive == nullptr) return; 	// 如果没有活动窗口则和父类一起销毁dialog->setParent(pActive);		// 转移父窗口}, Qt::UniqueConnection);
Windows强制销毁的底层原理
  1. 窗口树结构

    父窗口
    子窗口1
    子窗口2
    孙窗口

    父窗口销毁时,整个子树被递归销毁

  2. 系统消息流

    DestroyWindow(hParent) 调用
    ├── 发送WM_DESTROY到hParent
    ├── 递归调用DestroyWindow(hChild1)
    ├── 递归调用DestroyWindow(hChild2)
    └── 最后释放内存
    

    (参考:Windows消息序列)

  3. Qt的应对机制

    • QWidgetwinId()创建原生窗口句柄
    • 父子窗口关系通过SetParent()API建立
    • 系统级销毁无法被Qt拦截,只能通过提前解除父子关系避免

    (参考:Windows消息序列)

  4. Qt的应对机制

    • QWidgetwinId()创建原生窗口句柄
    • 父子窗口关系通过SetParent()API建立
    • 系统级销毁无法被Qt拦截,只能通过提前解除父子关系避免

关键结论:Windows的强制销毁是系统级行为,Qt应用必须通过主动管理窗口生命周期来规避状态不一致问题。对于不频繁使用的等待对话框,推荐使用每次创建的模式,或结合QPointer的状态验证机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87598.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87598.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87598.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文详解Character AI:实用指南+ ChatGPT、Gemini对比分析

本指南将深入剖析Character AI的运行机制、功能特性及其存在的局限性。 近年来&#xff0c;生成式人工智能领域发展态势迅猛&#xff0c;其应用范畴已远超单纯的文本生成领域。在众多备受瞩目的新兴平台中&#xff0c;Character AI是一款支持用户以对话形式与人工智能生成角色…

遗传算法的原理与实现示例

遗传算法是一种受生物进化理论启发的随机优化算法&#xff0c;其核心思想是模拟自然界中 “物竞天择、适者生存” 的进化过程&#xff0c;通过对候选解的迭代优化&#xff0c;找到问题的最优解。 一、核心思想 遗传算法将优化问题的候选解视为生物群体中的“个体”&#xff0c…

centos7 ping127.0.0.1不通

ping 127.0.0.1&#xff0c;localhost和本地ip都不通&#xff0c;所有的配置也是正确的 检查下是否禁止了ping vim /proc/sys/net/ipv4/icmp_echo_ignore_all 内容为 1 禁止ping 内容为0 开启ping sysctl -w net.ipv4.icmp_echo_ignore_all0 变更以上设置即可

【无标题】JavaScript入门

JS 1.JS引入方式 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>JS-引入方式</title><!-- …

(JAVA)自建应用调用企业微信API接口,实现消息推送

建议先简单了解企业微信开发者中心文档&#xff1a;开发前必读 - 文档 - 企业微信开发者中心 了解一下企业微信调用接口的基础参数&#xff1a;基本概念介绍 - 文档 - 企业微信开发者中心 本篇每个步骤都会跟着官网文档走&#xff0c;都会贴上相关链接&#xff0c;看完本篇文…

P/Invoke 在默认封送(marshalling)规则下,常见托管 ⇄ 非托管类型的对应关系

下表整理了 P/Invoke 在默认封送&#xff08;marshalling&#xff09;规则下&#xff0c;常见托管 ⇄ 非托管类型的对应关系。 内容主要依据微软官方 Marshalling Data with Platform Invoke 文档&#xff0c;并补充了常见指针&#xff0f;句柄用法与字符串缓冲区&#xff…

2.isaacsim4.2 教程-初识OmniGraph

1. OmniGraph&#xff08;视觉编程&#xff09; OmniGraph 是 Omniverse 的可视化编程框架。它提供了一个图状结构&#xff0c;将 Omniverse 内多个系统的功能节点串联起来&#xff1b;同时也是一个计算框架&#xff0c;允许你编写高度自定义的节点&#xff0c;将自己的功能无…

MonoGame 游戏开发框架日记 -03

第三章&#xff1a;创建类库 内容介绍 主要内容&#xff1a;创建Core类并编写 创建这个类主要是为了后续开发方便&#xff0c;并介绍游戏开发中的一种非常重要编程模式 单例模式&#xff0c;以及了解MonoGame基本图形渲染知识单例模式&#xff1a; 第一步我们得先了解什么是单例…

AES 256 CBC加密和解密

AES-256-CBC 是一种对称加密算法&#xff0c;使用 256位密钥 和 CBC&#xff08;Cipher Block Chaining&#xff09;模式。它的典型使用场景包括对敏感信息进行加密存储或传输。下面是 AES-256-CBC 的加密与解密的 Python 示例&#xff0c;使用 pycryptodome 库&#xff1a; &a…

Git 版本控制完全指南:从入门到精通

Git 版本控制完全指南&#xff1a;从入门到精通 作为当今最流行的分布式版本控制系统&#xff0c;Git 已经成为开发者必备的技能之一。无论你是独立开发者还是团队协作&#xff0c;Git 都能帮助你高效管理代码版本。本文将带你从零开始&#xff0c;逐步掌握 Git 的核心概念和常…

408第三季part2 - 计算机网络 - 计算机网络分层结构

理解 PCI会放一些控制信息&#xff0c;源地址目的地址都在里面 SDU是放的数据 整个加起来是PDU 每一层的SDU都是上一层的PDU 看一看 也是简单看一看就行 网络层有时候也叫IP数据报 这里断点下载的意思就是&#xff0c;你下载东西的时候网络断了&#xff0c;再连回来的时候会接…

打开摄像头,服务器和客户端传输摄像头图像数据

1&#xff1a;Camera Server 主要功能&#xff0c;打开摄像头&#xff0c;接收客户端请求 接收到客户端请求“R”字符后开始传输摄像头图像。 #include "mainwindow.h" #include "ui_mainwindow.h"#include<QDebug>MainWindow::MainWindow(QWidget…

Android实现获取前台应用信息

Android实现获取前台应用信息 1.前言&#xff1a; 之前需要获取在后台运行的App信息&#xff0c;比如包名、版本这些常规的&#xff0c;今天是讲解获取在前台的App信息&#xff0c;虽然App在前台&#xff0c;但是具体的信息可能不知道&#xff0c;今天就尝试获取一下&#xf…

快讯|美团即时零售日订单已突破1.2亿,餐饮订单占比过亿

据美团内网公布信息显示&#xff0c;截至22时54分&#xff0c;美团即时零售当日订单已经突破了1.2亿单&#xff0c;其中&#xff0c;餐饮订单已超过1亿单。 值得注意的是&#xff0c;就在当晚20时45分&#xff0c;美团内网曾显示即时零售日订单突破了1亿。这也意味着&#xff…

pycharm2018配置gitee操作

一、gitee介绍及下载安装 gitee介绍&#xff1a; gitee别名码云&#xff0c;是中国的一个代码托管平台&#xff0c;类似于GitHub&#xff0c;基于Git技术&#xff0c;提供远程仓库托管、协作功能和开源社区服务&#xff0c;优势包括访问速度快、本地化服务和政策合规git和gite…

数据结构——栈的讲解(超详细)

数据结构——栈的讲解&#xff08;超详细&#xff09;-腾讯云开发者社区-腾讯云 #include"Stack.h" void STInit(ST* ps) {ps->arr NULL;ps->capacity ps->top 0; //总空间个数和有用空间个数都初始化为0 }void STDestroy(ST* ps) {if (ps -> arr) …

MySQL允许root用户远程连接

注意&#xff1a;在实际生产环境中&#xff0c;允许root用户从任意主机&#xff08;‘%’&#xff09;连接存在安全风险&#xff0c;建议使用强密码并限制访问IP&#xff0c;或者创建具有必要权限的单独用户用于远程连接。MySQL 配置远程连接指南 1. 登录 MySQL 服务器 mysql -…

STM32的 syscalls.c 和 sysmem.c

syscalls.c 是 STM32CubeIDE 自动生成的标准系统调用适配文件&#xff0c;用于裸机环境下支持 newlib 标准库&#xff08;如 printf, scanf, malloc&#xff09;的运行。这份文件提供了标准库运行所需的最小系统调用实现。现在我来逐段解析其作用&#xff0c;并补充你可能需要修…

Java零基础笔记01(JKD及开发工具IDEA安装配置)

1.Java简介 Java是一种广泛使用的计算机编程语言&#xff0c;由美国的Sun Microsystems公司&#xff08;Stanford University Network&#xff09;在1995年推出。Java以其跨平台、面向对象、安全性高等特点&#xff0c;广泛应用于企业级应用开发、移动应用开发等领域。2009年&a…

Spark SQL架构及高级用法

Spark SQL 架构概述 架构核心组件 API层&#xff08;用户接口&#xff09; 输入方式&#xff1a;SQL查询&#xff1b;DataFrame/Dataset API。统一性&#xff1a; 所有接口最终转换为逻辑计划树&#xff08;Logical Plan&#xff09;&#xff0c;进入优化流程。 编译器层&…