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
相关产品推荐
相关产品推荐

