You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GVariantBuilder构建空a{sv}字典时g_variant_builder_end崩溃原因及解决

问题:GDBus构建空a{sv}字典导致崩溃的原因及解决方法

我尝试使用C结合GDBus实现BLE广告功能,调用org.bluez.LEAdvertisingManager1.RegisterAdvertisement方法时,希望将第二个a{sv}类型参数设为空字典,编写了如下代码:

#include <gio/gio.h>
#include <stdlib.h>

static const char *LE_ADVERTISING_INTERFACE = "org.bluez.LEAdvertisement1";

static const gchar introspection_xml[] =
  "<node>"
  "  <interface name='org.freedesktop.DBus.Properties'>"
  "    <method name='GetAll'>"
  "      <arg direction='in' type='s' name='interface'/>"
  "      <arg direction='out' type='a{sv}' name='properties'/>"
  "    </method>"
  "  </interface>"
  "  <interface name='org.bluez.LEAdvertisement1'>"
  "    <method name='Release'/>"
  "  </interface>"
  "</node>";

static GVariant *build_advertisement_properties() {
    GVariantBuilder builder;
    g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));

    g_variant_builder_add(&builder, "{sv}", "Type", g_variant_new_string("peripheral"));
    g_variant_builder_add(&builder, "{sv}", "LocalName", g_variant_new_string("HelloPi"));
    g_variant_builder_add(&builder, "{sv}", "MinInterval", g_variant_new_uint16(0x0060));
    g_variant_builder_add(&builder, "{sv}", "MaxInterval", g_variant_new_uint16(0x00F0));

    return g_variant_new("a{sv}", &builder);
}

static void handle_method_call(GDBusConnection *conn,
                               const gchar *sender,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *method_name,
                               GVariant *parameters,
                               GDBusMethodInvocation *invocation,
                               gpointer user_data)
{
    g_print("sender: %s\n", sender);
    g_print("object_path: %s, interface_name: %s, method_name: %s\n", object_path, interface_name, method_name);

    if (g_strcmp0(method_name, "GetAll") == 0) {
        const gchar *req_iface;
        g_variant_get(parameters, "(&s)", &req_iface);
        if (g_strcmp0(req_iface, LE_ADVERTISING_INTERFACE) == 0) {
            GVariant *props = build_advertisement_properties();
            g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", props));
        } else {
            g_dbus_method_invocation_return_error(invocation, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Invalid interface");
        }
    } else if (g_strcmp0(method_name, "Release") == 0) {
        g_print("Advertisement Released\n");
        g_dbus_method_invocation_return_value(invocation, NULL);
    }
}

static const GDBusInterfaceVTable interface_vtable = {
    .method_call = handle_method_call,
    .get_property = NULL,
    .set_property = NULL
};

// 异步回调函数
static void on_advertisement_registered(GObject *source_object,
                                        GAsyncResult *res,
                                        gpointer user_data)
{
    GError *error = NULL;
    GVariant *result = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, &error);
    if (error) {
        g_printerr("RegisterAdvertisement failed: %s\n", error->message);
        g_error_free(error);
    } else {
        g_print("Advertisement registered successfully (async)!\n");
        g_variant_unref(result);
    }
}

int main() {
    GMainLoop *loop;
    GDBusConnection *connection;
    GDBusNodeInfo *introspection_data;
    GError *error = NULL;
    guint reg_id1, reg_id2;
    const gchar *adv_path = "/org/bluez/example/advertisement";

    introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, &error);
    if (!introspection_data) {
        g_printerr("Failed to parse introspection XML: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if (!connection) {
        g_printerr("Failed to connect to system bus: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    // 注册 Properties 接口
    reg_id1 = g_dbus_connection_register_object(
        connection,
        adv_path,
        introspection_data->interfaces[0],
        &interface_vtable,
        NULL, NULL, &error);

    // 注册 LEAdvertisement1 接口
    reg_id2 = g_dbus_connection_register_object(
        connection,
        adv_path,
        introspection_data->interfaces[1],
        &interface_vtable,
        NULL, NULL, &error);

    if (reg_id1 == 0 || reg_id2 == 0) {
        g_printerr("Failed to register DBus object: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    // 创建 LEAdvertisingManager1 代理
    GDBusProxy *proxy = g_dbus_proxy_new_sync(connection,
                                              G_DBUS_PROXY_FLAGS_NONE,
                                              NULL,
                                              "org.bluez",
                                              "/org/bluez/hci0",
                                              "org.bluez.LEAdvertisingManager1",
                                              NULL, &error);

    if (!proxy) {
        g_printerr("Failed to create proxy: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    g_print("Registering advertisement (async)...\n");

    GVariantBuilder empty_dict;
    g_variant_builder_init(&empty_dict, G_VARIANT_TYPE("a{sv}"));
    // 异步调用 RegisterAdvertisement
    g_dbus_proxy_call(proxy,
                      "RegisterAdvertisement",
                      g_variant_new("(oa{sv})", adv_path, g_variant_builder_end(&empty_dict)),
                      G_DBUS_CALL_FLAGS_NONE,
                      -1,
                      NULL,
                      on_advertisement_registered,
                      NULL);

    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    // cleanup
    g_object_unref(proxy);
    g_main_loop_unref(loop);
    g_dbus_node_info_unref(introspection_data);
    g_object_unref(connection);

    return 0;
}

程序运行后崩溃,报错信息如下:

pi@pi:~ $ ./gdbus_adv
Registering advertisement (async)...

(process:2556): GLib-CRITICAL **: 10:26:25.875: g_variant_builder_end: assertion 'valid_builder' failed

(process:2556): GLib-CRITICAL **: 10:26:25.876: g_variant_get_type: assertion 'value != NULL' failed

(process:2556): GLib-CRITICAL **: 10:26:25.876: g_variant_type_is_array: assertion 'g_variant_type_check (type)' failed

(process:2556): GLib-CRITICAL **: 10:26:25.876: g_variant_get_type_string: assertion 'value != NULL' failed

(process:2556): GLib-ERROR **: 10:26:25.876: g_variant_new: expected array GVariantBuilder but the built value has type '(null)'
Trace/breakpoint trap

崩溃原因

g_variant_builder_end()在没有向builder添加任何元素时,会返回NULL。而g_variant_new()无法接受NULL作为数组或字典类型的参数,它期望一个有效的GVariant对象。直接将空builder的end()结果传入g_variant_new(),会触发GLib内部的断言检查失败,最终导致程序崩溃。

构建空a{sv}字典的正确方式

有两种简洁可靠的方式可以生成空的a{sv}字典:

方法1:使用g_variant_new_parsed()

这个函数可以直接通过字符串语法解析生成GVariant,空字典用{}表示:

g_variant_new_parsed("{}")

方法2:直接用g_variant_new()构造

对于数组/字典类型,传入NULL作为元素列表即可生成空容器:

g_variant_new("a{sv}", NULL)

修改后的调用代码

将main函数中构建空字典并调用的代码替换为以下任意一种即可:

方式1示例:

g_print("Registering advertisement (async)...\n");

// 异步调用 RegisterAdvertisement,使用空字典参数
g_dbus_proxy_call(proxy,
                  "RegisterAdvertisement",
                  g_variant_new("(oa{sv})", adv_path, g_variant_new_parsed("{}")),
                  G_DBUS_CALL_FLAGS_NONE,
                  -1,
                  NULL,
                  on_advertisement_registered,
                  NULL);

方式2示例:

g_print("Registering advertisement (async)...\n");

// 异步调用 RegisterAdvertisement,使用空字典参数
g_dbus_proxy_call(proxy,
                  "RegisterAdvertisement",
                  g_variant_new("(oa{sv})", adv_path, g_variant_new("a{sv}", NULL)),
                  G_DBUS_CALL_FLAGS_NONE,
                  -1,
                  NULL,
                  on_advertisement_registered,
                  NULL);

这两种方式都能正确生成符合D-Bus要求的空a{sv}字典,避免NULL导致的断言失败和崩溃。

内容的提问来源于stack exchange,提问作者NameisNone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 08:50:54