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

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, &notification_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(&amp;args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &amp;array);
    dbus_message_iter_close_container(&amp;args, &amp;array);
    DBusMessageIter dict_entry;
    const char *key = "";
    dbus_uint32_t value = 0;
    dbus_message_iter_open_container(&amp;args, DBUS_TYPE_DICT_ENTRY, NULL, &amp;dict_entry);
    dbus_message_iter_append_basic(&amp;dict_entry, DBUS_TYPE_STRING, &amp;key);
    DBusMessageIter variant;
    dbus_message_iter_open_container(&amp;dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT32_AS_STRING, &amp;variant);
    dbus_message_iter_append_basic(&amp;variant, DBUS_TYPE_UINT32, &amp;value);
    dbus_message_iter_close_container(&amp;dict_entry, &amp;variant);
    dbus_message_iter_close_container(&amp;args, &amp;dict_entry);
    dbus_message_iter_append_basic(&amp;args, DBUS_TYPE_INT32, &amp;timeout);
    DBusPendingCall* pending;
    
    dbus_connection_send_with_reply(conn, message, &amp;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, &amp;args2))
        fprintf(stderr, "message has no arugments!\n");
    else if (DBUS_TYPE_UINT32 != (arg=dbus_message_iter_get_arg_type(&amp;args2))) {
        fprintf(stderr, "Argument is not int!\n");
        switch(arg){
            case DBUS_TYPE_STRING:
                char *string;
                dbus_message_iter_get_basic(&amp;args2, &amp;string);
                fprintf(stderr, "ITS A STRING: %s\n", string);
                break;
    }}
    else { 
        dbus_message_iter_get_basic(&amp;args2, &amp;id);
        printf("ID: %d\n", id);
    }

    dbus_message_unref(message);
    dbus_connection_close(conn);

    return 0;
}
问题分析与修复

核心问题

  1. 字典参数签名错误:Notify方法的第七个参数是a{sv}类型(字符串到变体的字典数组),原代码打开字典容器时未指定正确签名"{sv}",导致通知服务无法解析参数,直接断开连接。
  2. 参数传递错误:第二个参数应为replaces_id,原代码误传notification_id(虽值为0,但不符合方法定义)。
  3. 错误检查缺失:未对DBus操作(如消息创建、参数添加)的返回值做失败判断,无法定位中间环节问题。
  4. 回复类型不匹配:用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, &notification_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š

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:01:20