当前位置: 首页 > news >正文

Qt窗口被外部(非Qt内部机制)强制销毁,第二次再重复使用不显示

在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.lryc.cn/news/580371.html

相关文章:

  • cloudflare配合github搭建免费开源影视LibreTV一个独享视频网站 详细教程
  • vue3 el-input el-select 非空校验
  • 每日学习问题记录
  • DVWA靶场通关笔记-验证码绕过reCAPTCHA(High级别)
  • vue中添加原生右键菜单
  • 【零基础学AI】第24讲:卷积神经网络(CNN)架构设计
  • 【无标题】Go语言中的反射机制 — 元编程技巧与注意事项
  • 3dmax物理材质转换标准材质,物理材质转VR材质,VR材质转标准材质3dmax物理材质转标准材质插件
  • 电脑休眠设置
  • c++ python 共享内存
  • 后端树形结构
  • STM32F103RCTx的PWM输出控制电机
  • js游戏简单修改
  • React Native 开发环境搭建--mac--android--奔溃的一天
  • Hinge×亚矩云手机:以“深度连接”为名,重构云端社交的“真实感”
  • CSS02:四种CSS导入方式
  • pyspark大规模数据加解密优化实践
  • Python小工具之PDF合并
  • 数据结构:多维数组在内存中的映射(Address Mapping of Multi-dimensional Arrays)
  • IDEA中application.yml配置文件不自动提示解决办法
  • 如何在IntelliJ IDEA中设置数据库连接全局共享
  • 从“电话催维修“到“手机看进度“——售后服务系统开发如何重构客户体验
  • CppCon 2018 学习:Surprises In Object Lifetime
  • Kotlin 协程:Channel 与 Flow 深度对比及 Channel 使用指南
  • 【ES6】Latex总结笔记生成器(网页版)
  • Jenkins Pipeline(二)
  • 【Elasticsearch】深度分页及其替代方案
  • 【openp2p】 学习2:源码阅读P2PNetwork和P2PTunnel
  • 【STM32实践篇】:GPIO 详解
  • 网络资源模板--基于Android Studio 实现的极简天气App