GTK3封装GetOpenFileName时gtk_init崩溃,求修复或GTK4实现方案
修复GTK3文件选择对话框崩溃问题
解决GTK3崩溃的几个关键修复点:
- 延迟初始化GTK
全局静态对象s_gtkInit会在程序启动时自动调用gtk_init,这个时机可能早于其他依赖库初始化,或在非主线程执行,直接导致崩溃。改成第一次调用对话框时再初始化:
// 移除全局静态GtkInit类,替换为线程安全的延迟初始化 static void ensureGtkInit() { static std::once_flag initFlag; std::call_once(initFlag, []() { int argc = 0; gtk_init(&argc, nullptr); SLOG_STMI() << "init gtk done"; }); } // 在nativeFileDialog开头调用 ensureGtkInit();
- 修正父窗口关联逻辑
native2GtkWindow创建临时GtkWindow并绑定外部HWND的方式存在资源泄漏和窗口管理冲突,改用更安全的实现:
static GtkWindow* native2GtkWindow(HWND winid) { if (!winid) return nullptr; GdkDisplay* display = gdk_display_get_default(); GdkWindow* gdk_wnd = gdk_x11_window_foreign_new_for_display(display, winid); if (!gdk_wnd) return nullptr; // 尝试获取已有顶层窗口,不存在则返回NULL GtkWidget* widget = gtk_widget_get_toplevel(GTK_WIDGET(gdk_wnd)); return GTK_IS_WINDOW(widget) ? GTK_WINDOW(widget) : nullptr; }
确保GTK操作在主线程执行
GTK要求所有UI操作必须在主线程完成,如果程序在非主线程调用对话框,需通过消息队列切换到主线程执行。内存安全检查
- 修复
substr越界问题:
static char* substr(const char* src, int m, int n) { if (!src || m < 0) return nullptr; int srcLen = strlen(src); n = std::min(n, srcLen); if (m >= n) return nullptr; int len = n - m; char* dest = (char*)malloc(sizeof(char) * (len + 1)); if (!dest) return nullptr; strncpy(dest, src + m, len); dest[len] = '\0'; return dest; }
- 检查文件名获取的空指针:
const char* filename = gtk_file_chooser_get_filename(chooser); if (filename) { **filenames = strdup(filename); } else { **filenames = nullptr; ret = false; }
GTK4实现类似GetOpenFileName的文件选择对话框
GTK4废弃了GtkFileChooserDialog,改用GtkFileDialog,API更简洁,以下是完整实现:
#include "gtkdlghelper.h" #include <gtk/gtk.h> #include <gdk/gdkx.h> #include "../log.h" #define kLogTag "gtk" namespace Gtk { enum class Mode { OPEN, SAVE, FOLDER }; static void ensureGtkInit() { static std::once_flag initFlag; std::call_once(initFlag, []() { int argc = 0; gtk_init(&argc, nullptr); SLOG_STMI() << "init gtk4 done"; }); } static GdkSurface* native2GdkSurface(HWND winid) { if (!winid) return nullptr; GdkDisplay* display = gdk_display_get_default(); return gdk_x11_surface_foreign_new_for_display(display, winid); } bool openGtkFileChooser(std::list<std::string>& files, HWND parent, Mode mode, const std::string& title, const std::string& file, const std::string& path, const std::string& filter, std::string* sel_filter, bool sel_multiple) { ensureGtkInit(); GtkFileDialog* dialog = gtk_file_dialog_new(); gtk_file_dialog_set_title(dialog, title.c_str()); // 设置初始路径或文件名 if (!path.empty()) { GFile* gPath = g_file_new_for_path(path.c_str()); gtk_file_dialog_set_initial_folder(dialog, gPath); g_object_unref(gPath); } if (!file.empty()) { gtk_file_dialog_set_initial_name(dialog, file.c_str()); } // 设置文件过滤器(适配Windows风格格式) if (!filter.empty() && mode != Mode::FOLDER) { GtkFileFilter* gtkFilter = gtk_file_filter_new(); std::stringstream ss(filter); std::string token; while (std::getline(ss, token, ';;')) { size_t openParen = token.find('('); size_t closeParen = token.find(')'); if (openParen != std::string::npos && closeParen != std::string::npos) { std::string name = token.substr(0, openParen); std::string patterns = token.substr(openParen + 1, closeParen - openParen - 1); gtk_file_filter_set_name(gtkFilter, name.c_str()); std::stringstream patternSs(patterns); std::string pattern; while (std::getline(patternSs, pattern, ' ')) { gtk_file_filter_add_pattern(gtkFilter, pattern.c_str()); } } } gtk_file_dialog_set_filter(dialog, gtkFilter); g_object_unref(gtkFilter); } // 根据模式执行对话框 GError* error = nullptr; gboolean ret = FALSE; if (mode == Mode::OPEN) { if (sel_multiple) { GListStore* store = gtk_file_dialog_open_multiple(dialog, parent ? GTK_WIDGET(native2GdkSurface(parent)) : nullptr, nullptr, &error); if (store) { ret = TRUE; guint nItems = g_list_model_get_n_items(G_LIST_MODEL(store)); for (guint i = 0; i < nItems; i++) { GFile* file = G_FILE(g_list_model_get_item(G_LIST_MODEL(store), i)); char* path = g_file_get_path(file); files.push_back(path); g_free(path); g_object_unref(file); } g_object_unref(store); } } else { GFile* selectedFile = gtk_file_dialog_open(dialog, parent ? GTK_WIDGET(native2GdkSurface(parent)) : nullptr, nullptr, &error); if (selectedFile) { ret = TRUE; char* path = g_file_get_path(selectedFile); files.push_back(path); g_free(path); g_object_unref(selectedFile); } } } else if (mode == Mode::SAVE) { GFile* selectedFile = gtk_file_dialog_save(dialog, parent ? GTK_WIDGET(native2GdkSurface(parent)) : nullptr, nullptr, &error); if (selectedFile) { ret = TRUE; char* path = g_file_get_path(selectedFile); files.push_back(path); g_free(path); g_object_unref(selectedFile); } } else if (mode == Mode::FOLDER) { GtkFileDialogFlags flags = GTK_FILE_DIALOG_SELECT_FOLDER; GFile* selectedFolder = gtk_file_dialog_open(dialog, parent ? GTK_WIDGET(native2GdkSurface(parent)) : nullptr, nullptr, &error); if (selectedFolder) { ret = TRUE; char* path = g_file_get_path(selectedFolder); files.push_back(path); g_free(path); g_object_unref(selectedFolder); } } if (error) { SLOG_STMI() << "File dialog error: " << error->message; g_error_free(error); } g_object_unref(dialog); return ret == TRUE; } static inline BYTE tobcolor(double v) { return (BYTE)(v * 255); } bool gtk_choose_color(HWND parent, COLORREF* out) { ensureGtkInit(); GtkColorDialog* dialog = gtk_color_dialog_new(); gtk_color_dialog_set_title(dialog, "Choose Color"); GdkRGBA initialColor = {0}; // 如需初始化颜色,可从out读取值赋值给initialColor GError* error = nullptr; GdkRGBA selectedColor; gboolean ret = gtk_color_dialog_choose_rgba(dialog, parent ? GTK_WIDGET(native2GdkSurface(parent)) : nullptr, &initialColor, nullptr, &error, &selectedColor); if (ret) { *out = RGBA(tobcolor(selectedColor.red), tobcolor(selectedColor.green), tobcolor(selectedColor.blue), tobcolor(selectedColor.alpha)); } if (error) { SLOG_STMI() << "Color dialog error: " << error->message; g_error_free(error); } g_object_unref(dialog); return ret == TRUE; } } // end of ns Gtk
说明
- GTK4的
GtkFileDialog采用同步调用,和GetOpenFileName行为一致 - 过滤器解析适配Windows风格格式(用
;;分隔不同过滤器组) - 父窗口关联通过
gdk_x11_surface_foreign_new_for_display获取对应GdkSurface传递给对话框 - 所有GTK操作通过
ensureGtkInit确保只初始化一次,且在第一次调用时执行
内容的提问来源于stack exchange,提问作者黄建雄
相关产品推荐
相关产品推荐

