如何在破坏父窗口之前关闭此GTK :: Messagedialog?

发布于 2025-02-08 06:04:19 字数 1862 浏览 3 评论 0原文

我目前正在尝试创建一个简单的GTKMM程序,该程序的按钮可以产生对话框。但是,我目前遇到问题,作为AppWindow类的破坏者,导致关闭对话框的Segfault。我检查unique_ptr是否是nullptr在呼叫关闭之前,但是如果对话框在主窗口之前已经关闭,则它将崩溃。我在这里采取正确的方法吗? unique_ptr在调用破坏者之前被释放吗?

main.c

#include "AppWindow.hpp"

#include <cstdio>
#include <cstdlib>
#include <gtkmm/application.h>

int main(int argc, char **argv) {
    std::shared_ptr<Gtk::Application> app = Gtk::Application::create("org.dylanweber.test");

    return app->make_window_and_run<AppWindow>(argc, argv);
}

appwindow.hpp

#include <gtkmm/button.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/window.h>
#include <iostream>

#pragma once

class AppWindow : public Gtk::Window {
  public:
    AppWindow();
    virtual ~AppWindow();

  private:
    Gtk::Button m_button;
    std::unique_ptr<Gtk::MessageDialog> dialog;

    void on_button_clicked();
};

appwindow.cpp

#include "AppWindow.hpp"

AppWindow::AppWindow() : m_button("Hello, world!") {
    this->m_button.set_margin(10);
    this->m_button.signal_clicked().connect(sigc::mem_fun(*this, &AppWindow::on_button_clicked));
    this->set_child(this->m_button);
}

AppWindow::~AppWindow() {
    if (this->dialog != nullptr) {
        this->dialog->close(); // seg fault here
    }
}

void AppWindow::on_button_clicked() {
    this->dialog = std::make_unique<Gtk::MessageDialog>(
        "Button clicked", false, Gtk::MessageType::QUESTION, Gtk::ButtonsType::OK);
    this->dialog->set_transient_for(*this);
    this->dialog->set_secondary_text("Hello");
    this->dialog->set_default_response(Gtk::ResponseType::OK);
    this->dialog->show();
}

I'm currently trying to create a simple Gtkmm program that has a button which spawns a dialog box. I'm currently having issues, however, as the destructor for the AppWindow class causes a segfault closing the dialog box. I check if the unique_ptr is nullptr before calling close, but even with that check it will crash if the dialog has already been closed before the main window has. Am I taking the correct approach here? Is the unique_ptr getting freed before the destructor is called?

main.c

#include "AppWindow.hpp"

#include <cstdio>
#include <cstdlib>
#include <gtkmm/application.h>

int main(int argc, char **argv) {
    std::shared_ptr<Gtk::Application> app = Gtk::Application::create("org.dylanweber.test");

    return app->make_window_and_run<AppWindow>(argc, argv);
}

AppWindow.hpp

#include <gtkmm/button.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/window.h>
#include <iostream>

#pragma once

class AppWindow : public Gtk::Window {
  public:
    AppWindow();
    virtual ~AppWindow();

  private:
    Gtk::Button m_button;
    std::unique_ptr<Gtk::MessageDialog> dialog;

    void on_button_clicked();
};

AppWindow.cpp

#include "AppWindow.hpp"

AppWindow::AppWindow() : m_button("Hello, world!") {
    this->m_button.set_margin(10);
    this->m_button.signal_clicked().connect(sigc::mem_fun(*this, &AppWindow::on_button_clicked));
    this->set_child(this->m_button);
}

AppWindow::~AppWindow() {
    if (this->dialog != nullptr) {
        this->dialog->close(); // seg fault here
    }
}

void AppWindow::on_button_clicked() {
    this->dialog = std::make_unique<Gtk::MessageDialog>(
        "Button clicked", false, Gtk::MessageType::QUESTION, Gtk::ButtonsType::OK);
    this->dialog->set_transient_for(*this);
    this->dialog->set_secondary_text("Hello");
    this->dialog->set_default_response(Gtk::ResponseType::OK);
    this->dialog->show();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

魄砕の薆 2025-02-15 06:04:19

评论中的建议是正确的,您应该将驱动器空白,而不是呼叫对话 - &gt; close()。要查看为什么,请注意,此函数是GTK C API函数的包装器gtk_window_close,以gtk :: window> window class class(从哪个gtk :: :: :: :: witch)定义。 Messagedialog通过gtk :: Dialog):):

// in gtk/gtkmm/window.cc
void Window::close()
{
  gtk_window_close(gobj());
}

gobj()返回内部gobject _ pointer(Objectbase Base <定义/code> class)指向C API的GTK句柄,当前类无需。该指针在gtk_window_close中使用:

// in gtk/gtkwindow.c

void
gtk_window_close (GtkWindow *window)
{
  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);

  if (!_gtk_widget_get_realized (GTK_WIDGET (window)))
    return;

  if (priv->in_emit_close_request)
    return;

  g_object_ref (window);

  if (!gtk_window_emit_close_request (window))
    gtk_window_destroy (window);

  g_object_unref (window);
}

// in gtk/gtkwidgetprivate.h

static inline gboolean
_gtk_widget_get_realized (GtkWidget *widget)
{
  return widget->priv->realized;
}

正如vs debugger在运行示例时所说的那样,segfault发生了,因为widget_gtk_widget_get_get_realized中的0。关闭对话框时已清除,因为默认情况下(通过CLOSE方法或关闭按钮)窗口意味着销毁它,因为您可以通过调用gtk_window_destroy来查看。内部gtk_window_close。使用GTK的C API GTK_WINDOW_DESTROY通过相当复杂的回调机制函数销毁此功能,可以适当地注册与内存管理有关的C ++功能。数据断点帮助精确地指出了gobject _将设置为0 - object :: destrion_notify _(该本身是从GTK的参考计数代码中间接调用的,因为对话对话的参考计数降至0) :

// gtk/gtkmm/object.cc
void Object::destroy_notify_()
{
  //Overriden.
  //GTKMM_LIFECYCLE

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Gtk::Object::destroy_notify_: this=%p, gobject_=%p\n", (void*)(Glib::ObjectBase*)this, (void*)gobject_);
  if(gobject_)
    g_warning("  gtypename=%s\n", G_OBJECT_TYPE_NAME(gobject_));
  #endif

  //Actually this function is called when the GObject is finalized, not when it's
  //disposed. Clear the pointer to the GObject, because otherwise it would
  //become a dangling pointer, pointing to a non-existent object.
  gobject_ = nullptr;

  if(!cpp_destruction_in_progress_) //This function might have been called as a side-effect of destroy_() when it called g_object_run_dispose().
  {
    if (!referenced_) //If it's manage()ed.
    {
      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Gtk::Object::destroy_notify_: before delete this.\n");
      #endif
      delete this; //Free the C++ instance.
    }
    else  //It's not managed, but the C gobject_ just died before the C++ instance..
    {
      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Gtk::Object::destroy_notify_: setting gobject_ to 0\n");
      #endif
    }
  }
}

因此,关闭对话框后,Messagedialog的内部句柄将被清除为0(顺便说一句,Messagedialog Destructor在此期间未调用,我也在调试器中检查了),并且由于您将其称为<代码>关闭假定不是,您会得到内存访问问题。如果gobject _已经清除了destructor,请正确工作,因此在关闭on_button_clicked关闭后,您不会在重新创建Messagedialog中遇到问题代码> std :: simelo_ptr 如果存在的话,请调用上一个对象的destructor),以及在gobject _的情况下仍然指向有效对象,因此即使它也不应遇到问题对话在娱乐前没有关闭。出于相同的原因,MessagedialogAppWindow中调用 destructor在窗口前是否关闭对话框的destructor destructor。

PS请注意,GTKMM .cc文件来自VCPKG buildetre,使用GTKMM的自定义预处理生成gmmproc gmmproc 工具在构建过程中,来自cccg source>源文件,因此您不会在存储库中找到它们。

Suggestion in the comments is right, you should leave your destructor empty and not call dialog->close() there. To see why, note that this function is a wrapper for GTK C API function gtk_window_close, defined like this for Gtk::Window class (from which Gtk::MessageDialog inherits through Gtk::Dialog):

// in gtk/gtkmm/window.cc
void Window::close()
{
  gtk_window_close(gobj());
}

Here gobj() returns internal gobject_ pointer (defined in ObjectBase class) pointing to C API's GTK handle, which current class incapsulates. This pointer is used inside gtk_window_close this way:

// in gtk/gtkwindow.c

void
gtk_window_close (GtkWindow *window)
{
  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);

  if (!_gtk_widget_get_realized (GTK_WIDGET (window)))
    return;

  if (priv->in_emit_close_request)
    return;

  g_object_ref (window);

  if (!gtk_window_emit_close_request (window))
    gtk_window_destroy (window);

  g_object_unref (window);
}

// in gtk/gtkwidgetprivate.h

static inline gboolean
_gtk_widget_get_realized (GtkWidget *widget)
{
  return widget->priv->realized;
}

As VS debugger told when running your example, segfault happens because widget is 0 in _gtk_widget_get_realized. It was cleared when dialog was closed, because by default closing (through close method or close button) a window means destroying it, as you can see e.g. by a call to gtk_window_destroy inside gtk_window_close. This destroy using GTK's C API gtk_window_destroy function through a pretty complex callback mechanism reaches appropriately registered C++ functions relating to memory management. Data breakpoint helped pinpoint exactly where gobject_ was set to 0 - Object::destroy_notify_ (which itself was indirectly called from reference counting code of GTK because dialog's reference count dropped to 0):

// gtk/gtkmm/object.cc
void Object::destroy_notify_()
{
  //Overriden.
  //GTKMM_LIFECYCLE

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Gtk::Object::destroy_notify_: this=%p, gobject_=%p\n", (void*)(Glib::ObjectBase*)this, (void*)gobject_);
  if(gobject_)
    g_warning("  gtypename=%s\n", G_OBJECT_TYPE_NAME(gobject_));
  #endif

  //Actually this function is called when the GObject is finalized, not when it's
  //disposed. Clear the pointer to the GObject, because otherwise it would
  //become a dangling pointer, pointing to a non-existent object.
  gobject_ = nullptr;

  if(!cpp_destruction_in_progress_) //This function might have been called as a side-effect of destroy_() when it called g_object_run_dispose().
  {
    if (!referenced_) //If it's manage()ed.
    {
      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Gtk::Object::destroy_notify_: before delete this.\n");
      #endif
      delete this; //Free the C++ instance.
    }
    else  //It's not managed, but the C gobject_ just died before the C++ instance..
    {
      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Gtk::Object::destroy_notify_: setting gobject_ to 0\n");
      #endif
    }
  }
}

So, after closing a dialog, MessageDialog's internal handle is cleared to 0 (BTW, MessageDialog destructor is not called during this, which I also checked in a debugger), and since you call a close function which assumes it isn't, you get memory access problems. Destructor correctly works in case gobject_ was already cleared, so you don't get problems with recreating MessageDialog in on_button_clicked after it was closed (where std::unique_ptr calls destructor for previous object if it's present), as well as in case gobject_ still points to valid object, so you shouldn't have problems with it even if dialog wasn't closed before recreation. For the same reasons, MessageDialog destructor called in AppWindow destructor is OK regardless of whether the dialog was closed before the window.

P.S. Note that the gtkmm .cc files here are from vcpkg buildtrees, generated using gtkmm's custom preprocessing gmmproc tool during build process from .ccg source files, so you won't find them in repositories.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文