Linux下C调用org.freedesktop.Notifications发通知遇DBus连接断开及回复异常
问题描述
在Linux下通过org.freedesktop.Notifications服务发送通知,D-Feet测试可正常执行,但C语言实现时出现错误Connection was disconnected before a reply was received。已修复段错误,但读取DBus回复参数时仍有异常,相关代码如下:
#include <stdio.h> #include <dbus/dbus.h> int main() { DBusError error; DBusConnection *conn; dbus_uint32_t serial = 0; char *app_name = "My App"; dbus_uint32_t replaces_id = 0; char *app_icon = ""; char *summary = "Notification from My App"; char *body = "This is a test notification from My App."; char **actions = NULL; dbus_int32_t timeout = -1; dbus_uint32_t notification_id = 0; dbus_error_init(&error); conn = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { fprintf(stderr, "Error connecting to the D-Bus session bus: %s\n", error.message); dbus_error_free(&error); return 1; } DBusMessage *message = dbus_message_new_method_call("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify"); DBusMessageIter args, array; dbus_message_iter_init_append(message, &args); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_name); dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, ¬ification_id); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_icon); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &summary); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &body); dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); dbus_message_iter_close_container(&args, &array); DBusMessageIter dict_entry; const char *key = ""; dbus_uint32_t value = 0; dbus_message_iter_open_container(&args, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key); DBusMessageIter variant; dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT32_AS_STRING, &variant); dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &value); dbus_message_iter_close_container(&dict_entry, &variant); dbus_message_iter_close_container(&args, &dict_entry); dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &timeout); DBusPendingCall* pending; dbus_connection_send_with_reply(conn, message, &pending, DBUS_TIMEOUT_INFINITE); dbus_connection_flush(conn); dbus_message_unref(message); dbus_pending_call_block(pending); message = dbus_pending_call_steal_reply(pending); dbus_pending_call_unref(pending); DBusMessageIter args2; int arg, id; if(!dbus_message_iter_init(message, &args2)) fprintf(stderr, "message has no arugments!\n"); else if (DBUS_TYPE_UINT32 != (arg=dbus_message_iter_get_arg_type(&args2))) { fprintf(stderr, "Argument is not int!\n"); switch(arg){ case DBUS_TYPE_STRING: char *string; dbus_message_iter_get_basic(&args2, &string); fprintf(stderr, "ITS A STRING: %s\n", string); break; }} else { dbus_message_iter_get_basic(&args2, &id); printf("ID: %d\n", id); } dbus_message_unref(message); dbus_connection_close(conn); return 0; }
问题分析与修复
核心问题
- 字典参数签名错误:
Notify方法的第七个参数是a{sv}类型(字符串到变体的字典数组),原代码打开字典容器时未指定正确签名"{sv}",导致通知服务无法解析参数,直接断开连接。 - 参数传递错误:第二个参数应为
replaces_id,原代码误传notification_id(虽值为0,但不符合方法定义)。 - 错误检查缺失:未对DBus操作(如消息创建、参数添加)的返回值做失败判断,无法定位中间环节问题。
- 回复类型不匹配:用
int类型接收uint32_t类型的通知ID,存在类型转换风险。
修复后的代码
#include <stdio.h> #include <dbus/dbus.h> int main() { DBusError error; DBusConnection *conn; char *app_name = "My App"; dbus_uint32_t replaces_id = 0; char *app_icon = ""; char *summary = "Notification from My App"; char *body = "This is a test notification from My App."; dbus_int32_t timeout = -1; dbus_uint32_t notification_id = 0; dbus_error_init(&error); // 连接会话总线 conn = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { fprintf(stderr, "连接D-Bus会话总线失败: %s\n", error.message); dbus_error_free(&error); return 1; } if (!conn) { fprintf(stderr, "无法获取D-Bus连接\n"); return 1; } // 创建Notify方法调用 DBusMessage *message = dbus_message_new_method_call( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); if (!message) { fprintf(stderr, "创建DBus消息失败\n"); dbus_connection_close(conn); return 1; } DBusMessageIter args, array, dict; dbus_message_iter_init_append(message, &args); // 依次添加参数 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_name)) goto cleanup; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &replaces_id)) goto cleanup; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_icon)) goto cleanup; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &summary)) goto cleanup; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &body)) goto cleanup; // 空actions数组 if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array)) goto cleanup; dbus_message_iter_close_container(&args, &array); // 空hints字典(正确指定a{sv}签名) if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING "sv" DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict)) goto cleanup; dbus_message_iter_close_container(&args, &dict); if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &timeout)) goto cleanup; // 发送消息并等待回复 DBusPendingCall* pending = NULL; if (!dbus_connection_send_with_reply(conn, message, &pending, DBUS_TIMEOUT_INFINITE)) goto cleanup; if (!pending) goto cleanup; dbus_connection_flush(conn); dbus_message_unref(message); dbus_pending_call_block(pending); message = dbus_pending_call_steal_reply(pending); dbus_pending_call_unref(pending); if (!message) { fprintf(stderr, "未收到回复\n"); goto cleanup_conn; } // 解析回复 dbus_message_iter_init(message, &args); int arg_type = dbus_message_iter_get_arg_type(&args); if (arg_type != DBUS_TYPE_UINT32) { fprintf(stderr, "回复参数类型错误,预期uint32,实际类型: %d\n", arg_type); if (arg_type == DBUS_TYPE_STRING) { char *err_str; dbus_message_iter_get_basic(&args, &err_str); fprintf(stderr, "错误信息: %s\n", err_str); } } else { dbus_message_iter_get_basic(&args, ¬ification_id); printf("通知ID: %u\n", notification_id); } dbus_message_unref(message); cleanup_conn: dbus_connection_close(conn); return 0; cleanup: fprintf(stderr, "DBus操作失败\n"); dbus_message_unref(message); dbus_connection_close(conn); return 1; }
关键修复点说明
- 字典签名修正:使用
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING "sv" DBUS_DICT_ENTRY_END_CHAR_AS_STRING指定正确的a{sv}类型签名,解决服务断开连接的核心问题。 - 参数顺序校正:将第二个参数改为
replaces_id,符合Notify方法的参数定义。 - 完善错误检查:对所有DBus操作添加失败判断,通过
goto统一处理错误流程,便于问题定位。 - 类型匹配优化:用
dbus_uint32_t类型接收通知ID,避免类型转换异常。
内容的提问来源于stack exchange,提问作者Adam Labuš
相关产品推荐
相关产品推荐

