Return `gtk_file_chooser_dialog_new` result

Simply trying to return the filename returned from a GtkFileChooserDialog doesn’t work.
For example, here’s the code:

        GtkWidget *dialog = gtk_file_chooser_dialog_new(
                title, NULL,
                GTK_FILE_CHOOSER_ACTION_OPEN,
                "Open",
                GTK_RESPONSE_ACCEPT,
                "Exit",
                GTK_RESPONSE_CANCEL,
                NULL
        );
        int result = gtk_dialog_run(GTK_DIALOG (dialog));
        if (result == GTK_RESPONSE_ACCEPT) {
                GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
                char *filename = gtk_file_chooser_get_filename (chooser);
                g_filename_to_utf8(
                        filename,
                        strlen(filename),
                        NULL, NULL,
                        NULL
                );
                gtk_widget_destroy(dialog);
                printf("Filename: %s", filename);
                return filename;
        } else if (result == GTK_RESPONSE_CANCEL) {
                gtk_main_quit();
                return NULL;
        }

The filename is indeed printed in the printf() call, but when I return it, NULL is returned instead. Why is this happening?

The filename is a local variable and is not valid anymore after your function returns. The simple correction would be to make it static. Or, you define it somewhere else and pass the pointer to it as parameter by calling the function that opens the file. BTW, the function gtk_file_chooser_get_filename allocates a piece of memory for the returned string so you should free it after use, otherwise there is a memory leak.

1 Like

I think I don’t understand your reply. (I generally write no C code :slight_smile: )

My feeling is, that he does not try to return (the address) a local stack allocated variable, which would be invalid of course. He seems to try to return the value contained in filename variable, which is a pointer obtained from gtk_file_chooser_get_filename(). As you said yourself, gtk_file_chooser_get_filename() allocates that string, so it should exist after the function returns? Another remark, his usage of g_filename_to_utf8() seems to be wrong, as that function does not work in place, but returns a string. May that be related to his problem? I am really happy that I never had a teacher that gave me such types of homework.

@Agelos Better try to provide a minimal but complete code example. That would make it easier to find the issue.

1 Like

Yes, you are absolutely right. The operation return filename; returns the pointer allocated by gtk_file_chooser_get_filename. And this pointer is valid even after the whole file open function has returned. The similar code (but without g_filename_to_utf8) works fine for me. The problem must be something else.

The filename is a local variable and is not valid anymore after your function returns.

But that’s the point; I don’t use it inside the function, I return it in another function. And considering the returned value is a heap pointer, why is the value destroyed after returning?

gtk_file_chooser_get_filename allocates a piece of memory for the returned string so you should free it after use, otherwise there is a memory leak.

I do that in the function that is calling this function.

Basically it’s a library I am writing and I test the code through an “unreleased” example. This is the function that calls the said function:

        const char *data = open_file_dialog_gtk(title, file_extensions); // The function in the question, file_extensions is ignored
        return data;

And then this function is called into the example:

const char* filename = open_file_dialog_gtk(
                "Open File Example",
                NULL
        );

        if (filename != NULL)
        {
                char buffer[MAX_BYTES];
                sprintf(buffer, "You selected the file '%s'.", filename);
                dialogbox(
                        "File Selected",
                        buffer,
                );
        } else {
                nvd_dialog_box_new(
                        "Cancelled",
                        "No file selected.",
                        NVD_DIALOG_WARNING
                );
        }

That’s more or less the entire code. I also tried copying the data onto the stack but no success.

E: Perhaps we should start considering whether this is a bug in Gtk?

A GTK bug seems to be unlikely. A C compiler issue may be possible, when you use a really strange compiler configuration. Computer programming is not writing some code and hoping that it works or that others fix the bugs for us. I have just spent more than four weeks fulltime (my summer holidays) to search for some obscure bugs in my tiny chess engine. For your file chooser, just create a tiny complete example like my GTK4 Nim example from GTK4 for Graphical User Interfaces and the issue will become obvious. It is really not that much work.

1 Like

The link you posted uses some programming language I’ve never seen in my life.

What I want to tell you is, that you should create a complete, compileable example like my one in your favorite language (C )and with your favorite GTK version :slight_smile:

I am sorry for the wrong guess.

The function g_filename_to_utf8 does not seem to be the cause for the issue even if it is not properly used. If I see it right, the first parameter is not altered in any way.

I share the suggestion made by Mr. Salewski to provide a compileable example. This can be a very simple application like the one here: https://discourse.gnome.org/t/problem-with-gtk-text-view-scroll-to-mark-and-gtk-text-view-scroll-to-iter/

Alright, here’s basically the example you asked for. Compiled with GCC, -fsanitize=address -Wall -Wextra $(pkg-config --cflags --libs gtk+-3.0) -O0 -ggdb3, tested on gdb for failure:

#include <gtk/gtk.h>

/* A macro to check if Gtk has been initialized. */
#define NVD_CHECK_GTK_INIT                                                     \
        if (!gtk_init_check(NULL, NULL)) {                                      \
                return NULL;                                                   \
        }

#ifndef NVDIALOG_MAXBUF
#define NVDIALOG_MAXBUF 4096
#endif /* NVDIALOG_MAXBUF */

/* THIS is the function that causes the problem. */
const char *nvd_open_file_dialog_gtk(const char *title,
                                     const char *file_extensions) {
        NVD_CHECK_GTK_INIT;

        /* Creating the dialog. */
        GtkWidget *dialog = gtk_file_chooser_dialog_new(
            title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "Open",
            GTK_RESPONSE_ACCEPT, "Cancel", GTK_RESPONSE_CANCEL, NULL);
        /* Running the dialog and checking the user's action. */
        int result = gtk_dialog_run(GTK_DIALOG(dialog));
        if (result == GTK_RESPONSE_ACCEPT) {
                GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
                char *filename = gtk_file_chooser_get_filename(chooser);
                /* For compatibility reasons, we need the filename in UTF-8
                 * format. */
                g_filename_to_utf8(filename, strlen(filename), NULL, NULL,
                                   NULL);
                /* The dialog should be destroyed at this stage. */
                gtk_widget_destroy(dialog);

                /* Copying the value to the stack. */
                char __buffer[NVDIALOG_MAXBUF];

                /* Avoiding a warning. */
                char *buffer = __buffer;
                return buffer;
        } else if (result == GTK_RESPONSE_CANCEL ||
                   result == GTK_RESPONSE_CLOSE) {
                gtk_main_quit();
                return NULL;
        }
        return NULL;
}

int main(int argc, char** argv)
{
    gtk_init(&argc, &argv); /* In reality I only pass argv[0] because its a library */
    const char* filename = nvd_open_file_dialog_gtk("Open file", NULL); /* We don't use the second parameter anyways. */
    if (!filename) return -1;
    puts(filename);
    return 0;
}

This code for some reason won’t segfault but it returns 1. Without printing anything.
If you want the actual library code, I can give you the GitHub repo to test it yourself.

I am not really good at C. But with this tiny fix it works fine for me on Linux:

#include <gtk/gtk.h>

/* A macro to check if Gtk has been initialized. */
#define NVD_CHECK_GTK_INIT                                                     \
        if (!gtk_init_check(NULL, NULL)) {                                      \
                return NULL;                                                   \
        }

#ifndef NVDIALOG_MAXBUF
#define NVDIALOG_MAXBUF 4096
#endif /* NVDIALOG_MAXBUF */

/* THIS is the function that causes the problem. */
const char *nvd_open_file_dialog_gtk(const char *title,
                                     const char *file_extensions) {
        NVD_CHECK_GTK_INIT;

        /* Creating the dialog. */
        GtkWidget *dialog = gtk_file_chooser_dialog_new(
            title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "Open",
            GTK_RESPONSE_ACCEPT, "Cancel", GTK_RESPONSE_CANCEL, NULL);
        /* Running the dialog and checking the user's action. */
        int result = gtk_dialog_run(GTK_DIALOG(dialog));
        if (result == GTK_RESPONSE_ACCEPT) {
                GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
                char *filename = gtk_file_chooser_get_filename(chooser);
                /* For compatibility reasons, we need the filename in UTF-8
                 * format. */
                /*g_filename_to_utf8(filename, strlen(filename), NULL, NULL,
                                   NULL);*/
                /* The dialog should be destroyed at this stage. */
                gtk_widget_destroy(dialog);

                /* Copying the value to the stack. */
                char __buffer[NVDIALOG_MAXBUF];

                /* Avoiding a warning. */
                char *buffer = __buffer;
                return filename;
        } else if (result == GTK_RESPONSE_CANCEL ||
                   result == GTK_RESPONSE_CLOSE) {
                gtk_main_quit();
                return NULL;
        }
        return NULL;
}

int main(int argc, char** argv)
{
    gtk_init(&argc, &argv); /* In reality I only pass argv[0] because its a library */
    const char* filename = nvd_open_file_dialog_gtk("Open file", NULL); /* We don't use the second parameter anyways. */
    if (!filename) return -1;
    puts(filename);
    return 0;
}
gcc `pkg-config gtk+-3.0 --cflags` t.c -o t `pkg-config --libs gtk+-3.0`
salewski@hx90 /tmp/hhh $ ls
t  t.c
salewski@hx90 /tmp/hhh $ ./t
0�q�!
salewski@hx90 /tmp/hhh $ gcc `pkg-config gtk+-3.0 --cflags` t.c -o t `pkg-config --libs gtk+-3.0`
salewski@hx90 /tmp/hhh $ ./t
/tmp/hhh/t.c
salewski@hx90 /tmp/hhh $ ./t
/home/salewski/treeview/test.c

First run was your strange unfixed code. Actually I can not remember well how the old GTK3 file dialog worked – please check if the fixed code works for you.

Well and this with utfname works too fine for me:

#include <gtk/gtk.h>

/* A macro to check if Gtk has been initialized. */
#define NVD_CHECK_GTK_INIT                                                     \
        if (!gtk_init_check(NULL, NULL)) {                                      \
                return NULL;                                                   \
        }

#ifndef NVDIALOG_MAXBUF
#define NVDIALOG_MAXBUF 4096
#endif /* NVDIALOG_MAXBUF */

/* THIS is the function that causes the problem. */
const char *nvd_open_file_dialog_gtk(const char *title,
                                     const char *file_extensions) {
        NVD_CHECK_GTK_INIT;

        /* Creating the dialog. */
        GtkWidget *dialog = gtk_file_chooser_dialog_new(
            title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "Open",
            GTK_RESPONSE_ACCEPT, "Cancel", GTK_RESPONSE_CANCEL, NULL);
        /* Running the dialog and checking the user's action. */
        int result = gtk_dialog_run(GTK_DIALOG(dialog));
        if (result == GTK_RESPONSE_ACCEPT) {
                GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
                char *filename = gtk_file_chooser_get_filename(chooser);
                /* For compatibility reasons, we need the filename in UTF-8
                 * format. */
                char *utfname = g_filename_to_utf8(filename, strlen(filename), NULL, NULL,
                                   NULL);
                /* The dialog should be destroyed at this stage. */
                gtk_widget_destroy(dialog);

                /* Copying the value to the stack. */
                char __buffer[NVDIALOG_MAXBUF];

                /* Avoiding a warning. */
                char *buffer = __buffer;
                return utfname;
        } else if (result == GTK_RESPONSE_CANCEL ||
                   result == GTK_RESPONSE_CLOSE) {
                gtk_main_quit();
                return NULL;
        }
        return NULL;
}

int main(int argc, char** argv)
{
    gtk_init(&argc, &argv); /* In reality I only pass argv[0] because its a library */
    const char* filename = nvd_open_file_dialog_gtk("Open file", NULL); /* We don't use the second parameter anyways. */
    if (!filename) return -1;
    puts(filename);
    return 0;
}

Of course later you have to care for freeing all the allocated strings. That is the result of coding in plain C.

This actually causes a segmentation fault for me

Actually, forget about the segmentation fault; It works fine for me. Seems like the library I’m writing somehow causes the segmentation fault.

You’re not doing anything with the return value of g_filename_to_utf8(), which contains the UTF-8 version of the file name. You need:

char *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);

Note: we use -1 for the filename length argument because it’ll do strlen(filename) for you.

This will not ever work: you’re returning a pointer to a value on the function’s stack.

I strongly encourage you to do two things:

  • read the GTK and GLib API references
  • use another programming language

If you insist on using C, then I recommend you read a book on the C programming language. “ANSI C, 2nd edition” is still a pretty solid book to learn the basics of the language, such as the difference between heap and stack allocations, and dealing with pointers.