DBUS源码剖析之DBusMessage消息参数处理

DBusMessage 消息参数处理

1. 概述

消息参数存储在消息体(body)中,使用 D-Bus 类型系统进行序列化。D-Bus 提供了两种方式处理参数:

  • 便捷函数dbus_message_append_args()dbus_message_get_args()(适用于简单场景)
  • 迭代器DBusMessageIter(适用于复杂类型和灵活控制)

2. 添加参数

2.1 使用 dbus_message_append_args

2.1.1 函数签名
dbus_bool_t dbus_message_append_args(
    DBusMessage *message,
    int first_arg_type,    // 第一个参数的类型
    ...);                  // 可变参数列表:类型-值对
2.1.2 基本用法

基本类型参数

// 准备要添加的参数值
const char *str = "Hello";      // 字符串参数
int num = 42;                    // 整数参数
dbus_bool_t flag = TRUE;         // 布尔参数

// 添加参数到消息
// 注意:每个参数需要传递类型代码和值的地址
dbus_message_append_args(
    msg,                          // 目标消息
    DBUS_TYPE_STRING, &str,       // 类型:字符串,值:str 的地址
    DBUS_TYPE_INT32, &num,        // 类型:32位整数,值:num 的地址
    DBUS_TYPE_BOOLEAN, &flag,     // 类型:布尔值,值:flag 的地址
    DBUS_TYPE_INVALID);           // 结束标记(必需,表示参数列表结束)

参数格式说明

  • 类型-值对:每个参数由类型代码和值的地址组成
  • 结束标记:最后一个参数必须是 DBUS_TYPE_INVALID,用于标识参数列表结束
  • 地址传递:基本类型需要传递值的地址(&value),因为函数内部需要读取值
  • 字符串特殊处理:字符串需要传递 const char**(即 &str),因为函数需要字符串指针的地址

为什么需要传递地址

  • 函数使用可变参数列表(va_list),需要知道值的类型和位置
  • 通过传递地址,函数可以读取实际的值
  • 对于字符串,需要传递指针的地址,因为字符串本身就是指针类型

完整示例

// 创建方法调用消息
DBusMessage *msg = dbus_message_new_method_call(
    "com.example.Service",
    "/com/example/Object",
    "com.example.Interface",
    "Method");

if (msg == NULL) {
    fprintf(stderr, "Failed to create message\n");
    return;
}

// 添加多个不同类型的参数
const char *name = "John";
int age = 30;
dbus_bool_t active = TRUE;
double salary = 50000.0;

if (!dbus_message_append_args(
        msg,
        DBUS_TYPE_STRING, &name,      // 参数1:字符串
        DBUS_TYPE_INT32, &age,        // 参数2:整数
        DBUS_TYPE_BOOLEAN, &active,   // 参数3:布尔值
        DBUS_TYPE_DOUBLE, &salary,    // 参数4:浮点数
        DBUS_TYPE_INVALID)) {         // 结束标记
    fprintf(stderr, "Failed to append arguments\n");
    dbus_message_unref(msg);
    return;
}

// 消息现在包含 4 个参数,可以发送
// dbus_connection_send(conn, msg, NULL);

dbus_message_unref(msg);
2.1.3 数组参数

固定长度类型数组

// 准备数组数据
const dbus_int32_t array[] = {1, 2, 3, 4, 5};
// 注意:必须使用指针变量,不能直接使用数组名
// 因为 C 语言中,数组名在某些上下文中会退化为指针,导致 &array != array
const dbus_int32_t *array_ptr = array;

// 添加数组参数
// 参数格式:DBUS_TYPE_ARRAY, 元素类型, 数组指针的地址, 元素数量
dbus_message_append_args(
    msg,
    DBUS_TYPE_ARRAY,              // 数组类型标识
    DBUS_TYPE_INT32,               // 元素类型:32位整数
    &array_ptr,                    // 数组指针的地址(注意是 &array_ptr,不是 &array)
    5,                             // 元素数量
    DBUS_TYPE_INVALID);            // 结束标记

为什么需要指针变量

  • C 语言中,&array 在某些情况下不等于 array
  • 函数需要数组指针的地址,而不是数组本身的地址
  • 使用中间指针变量可以确保类型正确

字符串数组

// 准备字符串数组
const char *strings[] = {"one", "two", "three"};
// 字符串数组本身就是指针数组,需要传递指针的地址
const char **strings_ptr = strings;

// 添加字符串数组参数
// 参数格式:DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, 字符串数组指针的地址, 元素数量
dbus_message_append_args(
    msg,
    DBUS_TYPE_ARRAY,              // 数组类型标识
    DBUS_TYPE_STRING,             // 元素类型:字符串
    &strings_ptr,                 // 字符串数组指针的地址(三级指针)
    3,                            // 元素数量
    DBUS_TYPE_INVALID);           // 结束标记

字符串数组的特殊性

  • 字符串数组是 const char*[] 类型
  • 需要传递 const char***(三级指针)
  • 函数内部会逐个复制每个字符串到消息体

完整示例

// 示例1:整数数组
const dbus_int32_t numbers[] = {10, 20, 30, 40};
const dbus_int32_t *numbers_ptr = numbers;

dbus_message_append_args(
    msg,
    DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &numbers_ptr, 4,
    DBUS_TYPE_INVALID);

// 示例2:字符串数组
const char *names[] = {"Alice", "Bob", "Charlie"};
const char **names_ptr = names;

dbus_message_append_args(
    msg,
    DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &names_ptr, 3,
    DBUS_TYPE_INVALID);

// 示例3:混合参数(数组 + 基本类型)
const dbus_uint32_t ids[] = {1, 2, 3};
const dbus_uint32_t *ids_ptr = ids;
const char *status = "active";

dbus_message_append_args(
    msg,
    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &ids_ptr, 3,  // 数组参数
    DBUS_TYPE_STRING, &status,                       // 字符串参数
    DBUS_TYPE_INVALID);

限制说明

  • 仅支持固定长度基本类型的数组:如 int32[]uint64[]double[]
  • 不支持 Unix 文件描述符数组:需要使用迭代器手动处理
  • 不支持复杂类型数组:如结构体数组、字典数组等,需要使用迭代器
  • 字符串数组特殊处理:虽然支持,但需要三级指针,使用相对复杂
2.1.4 Unix 文件描述符

基本用法

// 打开文件获取文件描述符
int fd = open("/path/to/file", O_RDONLY);
if (fd < 0) {
    perror("open");
    return;
}

// 添加文件描述符到消息
// 注意:传递 fd 的地址,函数内部会 dup() 这个 fd
dbus_message_append_args(
    msg,
    DBUS_TYPE_UNIX_FD, &fd,      // 类型:Unix 文件描述符,值:fd 的地址
    DBUS_TYPE_INVALID);

// 重要:可以立即关闭原始 fd
// 因为函数内部已经 dup() 了文件描述符,消息拥有自己的副本
close(fd);

工作原理说明

  1. 文件描述符传递

    • D-Bus 通过 Unix 域套接字的 SCM_RIGHTS 控制消息传递文件描述符
    • 消息体中存储的是 fd 在消息的 fd 数组中的索引,不是实际的 fd 值
  2. 内部处理流程

    // 函数内部的处理(简化版)
    int *fds = expand_fd_array(message, 1);  // 扩展消息的 fd 数组
    *fds = _dbus_dup(fd, NULL);               // dup() 文件描述符
    u = message->n_unix_fds;                  // 获取索引
    // 将索引写入消息体(而不是实际的 fd 值)
    _dbus_type_writer_write_basic(&writer, DBUS_TYPE_UNIX_FD, &u);
    
  3. 为什么可以立即关闭

    • 函数内部调用 dup() 创建了文件描述符的副本
    • 消息拥有自己的 fd 副本,独立于原始 fd
    • 原始 fd 可以立即关闭,不影响消息中的 fd

完整示例

// 示例1:传递单个文件描述符
int fd = open("/etc/passwd", O_RDONLY);
if (fd >= 0) {
    dbus_message_append_args(
        msg,
        DBUS_TYPE_UNIX_FD, &fd,
        DBUS_TYPE_INVALID);
    close(fd);  // 安全关闭
}

// 示例2:文件描述符 + 其他参数
int log_fd = open("/var/log/app.log", O_WRONLY | O_APPEND);
const char *message = "Log entry";

if (log_fd >= 0) {
    dbus_message_append_args(
        msg,
        DBUS_TYPE_STRING, &message,    // 字符串参数
        DBUS_TYPE_UNIX_FD, &log_fd,   // 文件描述符参数
        DBUS_TYPE_INVALID);
    close(log_fd);  // 安全关闭
}

// 示例3:错误处理
int fd = open("/path/to/file", O_RDONLY);
if (fd < 0) {
    perror("open");
    return;
}

if (!dbus_message_append_args(
        msg,
        DBUS_TYPE_UNIX_FD, &fd,
        DBUS_TYPE_INVALID)) {
    // 追加失败,关闭 fd
    close(fd);
    fprintf(stderr, "Failed to append file descriptor\n");
    return;
}

// 成功追加,可以关闭原始 fd
close(fd);

注意事项

  • 平台支持:需要编译时启用 HAVE_UNIX_FD_PASSING
  • 传输要求:必须使用 Unix 域套接字(AF_UNIX)
  • 资源管理:消息销毁时会自动关闭所有关联的 fd
  • 接收端处理:接收端会获得新的 fd(通过 recvmsg()),需要负责关闭
2.1.5 实现细节

内部实现流程

dbus_message_append_args() 内部使用可变参数列表和消息迭代器实现:

dbus_bool_t dbus_message_append_args(DBusMessage *message,
                                     int first_arg_type,
                                     ...)
{
  dbus_bool_t retval;
  va_list var_args;
  
  // 步骤 1: 初始化可变参数列表
  // va_start 用于获取可变参数的起始位置
  va_start(var_args, first_arg_type);
  
  // 步骤 2: 调用 valist 版本(便于语言绑定使用)
  retval = dbus_message_append_args_valist(message,
                                          first_arg_type,
                                          var_args);
  
  // 步骤 3: 清理可变参数列表
  va_end(var_args);
  
  return retval;
}

核心实现(valist 版本)

dbus_bool_t dbus_message_append_args_valist(DBusMessage *message,
                                            int first_arg_type,
                                            va_list var_args)
{
  int type;
  DBusMessageIter iter;
  
  // 步骤 1: 初始化追加迭代器
  // 将迭代器设置为写入模式,准备向消息体追加数据
  dbus_message_iter_init_append(message, &iter);
  
  // 步骤 2: 遍历可变参数列表
  type = first_arg_type;
  while (type != DBUS_TYPE_INVALID) {
    if (dbus_type_is_basic(type)) {
      // 情况 A: 基本类型(整数、字符串、布尔值等)
      const void *value;
      
      // 从可变参数列表中获取值(值的地址)
      value = va_arg(var_args, const void *);
      
      // 追加基本类型值到消息体
      if (!dbus_message_iter_append_basic(&iter, type, value))
        goto failed;  // 内存不足
    }
    else if (type == DBUS_TYPE_ARRAY) {
      // 情况 B: 数组类型
      int element_type;
      DBusMessageIter array;
      char buf[2];
      
      // 获取数组元素类型
      element_type = va_arg(var_args, int);
      
      // 构建元素类型签名字符串(用于容器)
      buf[0] = element_type;
      buf[1] = '\0';
      
      // 打开数组容器
      if (!dbus_message_iter_open_container(&iter,
                                            DBUS_TYPE_ARRAY,
                                            buf,
                                            &array))
        goto failed;
      
      if (dbus_type_is_fixed(element_type) &&
          element_type != DBUS_TYPE_UNIX_FD) {
        // 情况 B1: 固定长度类型数组(如 int32[])
        const void **value;
        int n_elements;
        
        // 获取数组指针和元素数量
        value = va_arg(var_args, const void **);
        n_elements = va_arg(var_args, int);
        
        // 一次性追加整个数组(高效)
        if (!dbus_message_iter_append_fixed_array(&array,
                                                   element_type,
                                                   value,
                                                   n_elements)) {
          // 失败时放弃容器
          dbus_message_iter_abandon_container(&iter, &array);
          goto failed;
        }
      }
      else if (_DBUS_TYPE_IS_STRINGLIKE(element_type)) {
        // 情况 B2: 字符串类数组(STRING、OBJECT_PATH、SIGNATURE)
        const char ***value_p;
        const char **value;
        int n_elements;
        int i;
        
        // 获取字符串数组指针(注意是三级指针)
        value_p = va_arg(var_args, const char***);
        n_elements = va_arg(var_args, int);
        value = *value_p;
        
        // 逐个追加字符串元素
        i = 0;
        while (i < n_elements) {
          if (!dbus_message_iter_append_basic(&array,
                                              element_type,
                                              &value[i])) {
            dbus_message_iter_abandon_container(&iter, &array);
            goto failed;
          }
          ++i;
        }
      }
      else {
        // 情况 B3: 不支持的数组类型(如复杂类型数组)
        // 需要使用迭代器手动处理
        dbus_message_iter_abandon_container(&iter, &array);
        goto failed;
      }
      
      // 关闭数组容器
      if (!dbus_message_iter_close_container(&iter, &array))
        goto failed;
    }
    
    // 步骤 3: 获取下一个参数类型
    type = va_arg(var_args, int);
  }
  
  return TRUE;
  
failed:
  return FALSE;
}

关键实现细节说明

  1. 可变参数处理

    • 使用 va_listva_startva_argva_end 处理可变参数
    • 参数格式:类型1, 值1, 类型2, 值2, ..., DBUS_TYPE_INVALID
    • 每个基本类型需要传递值的地址(&value
  2. 迭代器使用

    • 使用 dbus_message_iter_init_append() 初始化追加迭代器
    • 所有参数通过迭代器追加到消息体
    • 迭代器自动处理内存分配和对齐
  3. 数组处理

    • 固定长度类型:使用 append_fixed_array() 一次性追加(高效)
    • 字符串类型:逐个追加每个字符串元素
    • 复杂类型:不支持,需要使用迭代器手动处理
  4. 错误处理

    • 任何步骤失败都会跳转到 failed 标签
    • 数组处理失败时会调用 abandon_container() 清理
    • 返回 FALSE 表示失败
  5. 内存管理

    • 字符串值会被复制到消息体中
    • Unix 文件描述符会被 dup()
    • 失败时消息可能处于不一致状态,需要重新创建

性能考虑

  • 固定长度类型数组使用批量追加,比逐个追加更高效
  • 字符串数组需要逐个追加,因为每个字符串长度不同
  • 迭代器内部使用 DBusString,支持自动扩展

2.2 使用消息迭代器

2.2.1 初始化追加迭代器
DBusMessageIter iter;

// 初始化迭代器用于追加模式
// 迭代器会被设置为写入模式,准备向消息体追加数据
dbus_message_iter_init_append(message, &iter);

// 注意:
// - 迭代器分配在栈上,不需要手动释放
// - 可以多次调用此函数重新初始化迭代器
// - 每次调用都会重置迭代器到消息体末尾

初始化说明

  • 写入模式:迭代器被设置为 DBUS_MESSAGE_ITER_TYPE_WRITER
  • 位置:迭代器指向消息体末尾,新数据会追加到末尾
  • 字节序:自动使用编译器的字节序
  • 签名管理:迭代器会自动管理类型签名
2.2.2 追加基本类型
DBusMessageIter iter;
dbus_message_iter_init_append(message, &iter);

// 追加字符串
// 参数:迭代器、类型代码、值的地址
const char *str = "Hello";
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str)) {
    fprintf(stderr, "Failed to append string\n");
    return;
}
// 说明:字符串会被复制到消息体中,str 可以立即修改或释放

// 追加整数
// 注意:传递整数的地址
int num = 42;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &num)) {
    fprintf(stderr, "Failed to append integer\n");
    return;
}
// 说明:整数值会被复制到消息体中

// 追加布尔值
// 注意:dbus_bool_t 实际上是 uint32_t
dbus_bool_t flag = TRUE;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &flag)) {
    fprintf(stderr, "Failed to append boolean\n");
    return;
}
// 说明:布尔值在 D-Bus 中是 4 字节的 uint32(0 或 1)

// 追加浮点数
double value = 3.14159;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_DOUBLE, &value)) {
    fprintf(stderr, "Failed to append double\n");
    return;
}

关键点说明

  1. 地址传递:所有基本类型都需要传递值的地址(&value
  2. 返回值检查:函数返回 FALSE 表示失败(通常是内存不足)
  3. 数据复制:值会被复制到消息体中,原始变量可以修改
  4. 字符串处理:字符串会被复制,包括 null 终止符
  5. 类型验证:函数内部会验证类型和值的有效性(如 UTF-8 编码)
2.2.3 追加数组

基本数组追加

DBusMessageIter iter, sub;
dbus_message_iter_init_append(message, &iter);

// 步骤 1: 打开数组容器
// 参数:父迭代器、容器类型、元素类型签名、子迭代器
// "i" 表示元素类型是 int32
if (!dbus_message_iter_open_container(&iter,
                                      DBUS_TYPE_ARRAY,  // 容器类型:数组
                                      "i",              // 元素类型签名:int32
                                      &sub)) {          // 子迭代器(输出)
    fprintf(stderr, "Failed to open array container\n");
    return;
}

// 步骤 2: 添加数组元素
// 使用子迭代器追加每个元素
int values[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
    // 注意:传递 values[i] 的地址
    if (!dbus_message_iter_append_basic(&sub,
                                       DBUS_TYPE_INT32,
                                       &values[i])) {
        // 追加失败,放弃容器
        dbus_message_iter_abandon_container(&iter, &sub);
        fprintf(stderr, "Failed to append array element\n");
        return;
    }
}

// 步骤 3: 关闭数组容器
// 必须使用与 open_container 时相同的迭代器对
if (!dbus_message_iter_close_container(&iter, &sub)) {
    fprintf(stderr, "Failed to close array container\n");
    return;
}
// 说明:关闭容器会完成数组的序列化,包括长度和对齐

错误处理示例

DBusMessageIter iter, sub = DBUS_MESSAGE_ITER_INIT_CLOSED;

dbus_message_iter_init_append(message, &iter);

if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "i", &sub)) {
    // 打开失败,直接返回
    return;
}

// 追加元素...
int values[] = {1, 2, 3};
for (int i = 0; i < 3; i++) {
    if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &values[i])) {
        // 追加失败,放弃容器
        dbus_message_iter_abandon_container(&iter, &sub);
        return;
    }
}

// 关闭容器
if (!dbus_message_iter_close_container(&iter, &sub)) {
    // 关闭失败(内存不足),但容器已经部分写入
    // 消息可能处于不一致状态
    return;
}

字符串数组追加

DBusMessageIter iter, sub;
dbus_message_iter_init_append(message, &iter);

// 打开字符串数组容器
// "s" 表示元素类型是字符串
if (!dbus_message_iter_open_container(&iter,
                                      DBUS_TYPE_ARRAY,
                                      "s",  // 字符串类型签名
                                      &sub)) {
    return;
}

// 添加字符串元素
const char *names[] = {"Alice", "Bob", "Charlie"};
for (int i = 0; i < 3; i++) {
    // 注意:传递 names[i] 的地址(const char**)
    if (!dbus_message_iter_append_basic(&sub,
                                       DBUS_TYPE_STRING,
                                       &names[i])) {
        dbus_message_iter_abandon_container(&iter, &sub);
        return;
    }
}

// 关闭容器
if (!dbus_message_iter_close_container(&iter, &sub)) {
    return;
}

关键点说明

  1. 容器操作必须配对open_containerclose_container 必须使用相同的迭代器对
  2. 元素类型签名:必须提供正确的元素类型签名字符串
  3. 错误处理:任何步骤失败都应该调用 abandon_container() 清理
  4. 内存管理:容器内部会自动处理内存分配和对齐
  5. 性能考虑:对于固定长度类型数组,使用 append_fixed_array() 更高效
2.2.4 追加结构体

基本结构体追加

DBusMessageIter iter, sub;
dbus_message_iter_init_append(message, &iter);

// 步骤 1: 打开结构体容器
// 参数:父迭代器、容器类型、类型签名(NULL 表示自动推断)、子迭代器
// 注意:结构体的类型签名为 NULL,因为会根据实际添加的字段自动推断
if (!dbus_message_iter_open_container(&iter,
                                      DBUS_TYPE_STRUCT,  // 容器类型:结构体
                                      NULL,              // 类型签名:自动推断
                                      &sub)) {           // 子迭代器(输出)
    fprintf(stderr, "Failed to open struct container\n");
    return;
}

// 步骤 2: 添加结构体字段(按顺序)
// 结构体字段必须按顺序添加,不能跳过
const char *name = "John";
int age = 30;

// 字段1:字符串(姓名)
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 字段2:整数(年龄)
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &age)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 步骤 3: 关闭结构体容器
// 关闭时会自动完成结构体的序列化
if (!dbus_message_iter_close_container(&iter, &sub)) {
    fprintf(stderr, "Failed to close struct container\n");
    return;
}
// 说明:结构体在 D-Bus 中不需要对齐填充,字段连续存储

复杂结构体示例

// 结构体:(string, int32, boolean, double)
// 表示:姓名、年龄、是否激活、工资

DBusMessageIter iter, sub;
dbus_message_iter_init_append(message, &iter);

if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &sub)) {
    return;
}

// 字段1:姓名(字符串)
const char *name = "John Doe";
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 字段2:年龄(整数)
int age = 30;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &age)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 字段3:是否激活(布尔值)
dbus_bool_t active = TRUE;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &active)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 字段4:工资(浮点数)
double salary = 50000.0;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_DOUBLE, &salary)) {
    dbus_message_iter_abandon_container(&iter, &sub);
    return;
}

// 关闭结构体
if (!dbus_message_iter_close_container(&iter, &sub)) {
    return;
}

嵌套结构体示例

// 外层结构体:(string, (int32, string))
// 表示:姓名、地址(邮编、街道)

DBusMessageIter iter, outer, inner;
dbus_message_iter_init_append(message, &iter);

// 打开外层结构体
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &outer)) {
    return;
}

// 字段1:姓名
const char *name = "John";
if (!dbus_message_iter_append_basic(&outer, DBUS_TYPE_STRING, &name)) {
    dbus_message_iter_abandon_container(&iter, &outer);
    return;
}

// 字段2:地址(嵌套结构体)
if (!dbus_message_iter_open_container(&outer, DBUS_TYPE_STRUCT, NULL, &inner)) {
    dbus_message_iter_abandon_container(&iter, &outer);
    return;
}

// 地址字段1:邮编
int zipcode = 12345;
if (!dbus_message_iter_append_basic(&inner, DBUS_TYPE_INT32, &zipcode)) {
    dbus_message_iter_abandon_container(&outer, &inner);
    dbus_message_iter_abandon_container(&iter, &outer);
    return;
}

// 地址字段2:街道
const char *street = "Main St";
if (!dbus_message_iter_append_basic(&inner, DBUS_TYPE_STRING, &street)) {
    dbus_message_iter_abandon_container(&outer, &inner);
    dbus_message_iter_abandon_container(&iter, &outer);
    return;
}

// 关闭内层结构体
if (!dbus_message_iter_close_container(&outer, &inner)) {
    dbus_message_iter_abandon_container(&iter, &outer);
    return;
}

// 关闭外层结构体
if (!dbus_message_iter_close_container(&iter, &outer)) {
    return;
}

关键点说明

  1. 类型签名为 NULL:结构体的类型签名由实际添加的字段自动推断
  2. 字段顺序:字段必须按顺序添加,不能跳过中间字段
  3. 嵌套支持:结构体可以嵌套,形成复杂的层次结构
  4. 对齐规则:结构体字段按类型对齐,但结构体本身不需要填充
  5. 错误处理:嵌套结构体失败时需要按相反顺序放弃所有容器
2.2.5 追加字典

字典结构说明

  • D-Bus 字典是 array of dict_entry 类型
  • 每个 dict_entrystruct(key, value) 结构
  • 键通常是字符串,值可以是任意类型(通常使用 Variant)

基本字典追加

DBusMessageIter iter, dict, entry;
dbus_message_iter_init_append(message, &iter);

// 步骤 1: 打开字典容器(数组)
// "{sv}" 表示:数组的元素是 dict_entry,键是字符串(s),值是 Variant(v)
if (!dbus_message_iter_open_container(&iter,
                                      DBUS_TYPE_ARRAY,  // 容器类型:数组
                                      "{sv}",           // 元素类型:dict_entry<string, variant>
                                      &dict)) {         // 字典迭代器(输出)
    fprintf(stderr, "Failed to open dict container\n");
    return;
}

// 步骤 2: 添加第一个字典条目
if (!dbus_message_iter_open_container(&dict,
                                      DBUS_TYPE_DICT_ENTRY,  // 容器类型:字典条目
                                      NULL,                  // 类型签名:自动推断(struct(key, value))
                                      &entry)) {            // 条目迭代器(输出)
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 步骤 2.1: 添加键(字符串)
const char *key1 = "name";
if (!dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key1)) {
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 步骤 2.2: 添加值(Variant,包含字符串)
DBusMessageIter variant;
if (!dbus_message_iter_open_container(&entry,
                                      DBUS_TYPE_VARIANT,  // 容器类型:变体
                                      "s",                // 值的类型签名:字符串
                                      &variant)) {        // 变体迭代器(输出)
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

const char *value1 = "John";
if (!dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value1)) {
    dbus_message_iter_abandon_container(&entry, &variant);
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 关闭 Variant
if (!dbus_message_iter_close_container(&entry, &variant)) {
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 关闭字典条目
if (!dbus_message_iter_close_container(&dict, &entry)) {
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 步骤 3: 添加第二个字典条目(可以添加多个)
if (!dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) {
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

const char *key2 = "age";
if (!dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key2)) {
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 值:Variant,包含整数
if (!dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", &variant)) {
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

int age_value = 30;
if (!dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &age_value)) {
    dbus_message_iter_abandon_container(&entry, &variant);
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

if (!dbus_message_iter_close_container(&entry, &variant)) {
    dbus_message_iter_abandon_container(&dict, &entry);
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

if (!dbus_message_iter_close_container(&dict, &entry)) {
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

// 步骤 4: 关闭字典容器
if (!dbus_message_iter_close_container(&iter, &dict)) {
    fprintf(stderr, "Failed to close dict container\n");
    return;
}

辅助函数封装

// 辅助函数:添加字典条目(简化错误处理)
dbus_bool_t append_dict_entry(DBusMessageIter *dict,
                              const char *key,
                              const char *value_type,
                              const void *value)
{
    DBusMessageIter entry, variant;
    
    // 打开字典条目
    if (!dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry))
        return FALSE;
    
    // 添加键
    if (!dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key)) {
        dbus_message_iter_abandon_container(dict, &entry);
        return FALSE;
    }
    
    // 添加值(Variant)
    if (!dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, value_type, &variant)) {
        dbus_message_iter_abandon_container(dict, &entry);
        return FALSE;
    }
    
    // 根据类型添加值
    int type = value_type[0];
    if (!dbus_message_iter_append_basic(&variant, type, value)) {
        dbus_message_iter_abandon_container(&entry, &variant);
        dbus_message_iter_abandon_container(dict, &entry);
        return FALSE;
    }
    
    // 关闭 Variant
    if (!dbus_message_iter_close_container(&entry, &variant)) {
        dbus_message_iter_abandon_container(dict, &entry);
        return FALSE;
    }
    
    // 关闭字典条目
    return dbus_message_iter_close_container(dict, &entry);
}

// 使用示例
DBusMessageIter iter, dict;
dbus_message_iter_init_append(message, &iter);

if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict))
    return;

// 添加多个条目
const char *name = "John";
int age = 30;
dbus_bool_t active = TRUE;

if (!append_dict_entry(&dict, "name", "s", &name) ||
    !append_dict_entry(&dict, "age", "i", &age) ||
    !append_dict_entry(&dict, "active", "b", &active)) {
    dbus_message_iter_abandon_container(&iter, &dict);
    return;
}

if (!dbus_message_iter_close_container(&iter, &dict))
    return;

关键点说明

  1. 字典是数组:字典实际上是 array of dict_entry,不是独立类型
  2. 类型签名格式"{sv}" 表示 array<dict_entry<string, variant>>
  3. Variant 使用:值通常使用 Variant,允许不同类型混合
  4. 嵌套层次:字典 → 条目 → Variant → 值,共 4 层
  5. 错误处理:失败时需要按相反顺序放弃所有容器
  6. 性能考虑:字典条目可以按任意顺序添加,但通常按键排序

3. 读取参数

3.1 使用 dbus_message_get_args

3.1.1 函数签名
dbus_bool_t dbus_message_get_args(
    DBusMessage *message,
    DBusError *error,
    int first_arg_type,
    ...);
3.1.2 基本用法

基本类型参数读取

// 步骤 1: 声明变量用于存储读取的值
const char *str;    // 字符串指针(指向消息内部数据)
int num;            // 整数变量
DBusError error;    // 错误对象

// 步骤 2: 初始化错误对象(必须)
dbus_error_init(&error);

// 步骤 3: 读取参数
// 参数格式:消息、错误对象、类型1、地址1、类型2、地址2、...、DBUS_TYPE_INVALID
if (dbus_message_get_args(
        msg,                    // 要读取的消息
        &error,                 // 错误对象(用于接收错误信息)
        DBUS_TYPE_STRING, &str, // 类型:字符串,存储位置:str 的地址
        DBUS_TYPE_INT32, &num,  // 类型:整数,存储位置:num 的地址
        DBUS_TYPE_INVALID)) {   // 结束标记
    // 读取成功,使用参数
    printf("Received: %s, %d\n", str, num);
    
    // 注意:str 指向消息内部数据,消息释放后无效
    // num 是值的副本,可以安全使用
} else {
    // 读取失败,检查错误
    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error: %s\n", error.message);
        dbus_error_free(&error);  // 必须释放错误对象
    }
    return;
}

参数格式说明

  • 类型-地址对:每个参数由类型代码和存储地址组成
  • 地址传递:需要传递变量的地址(&variable),函数会将值写入该地址
  • 结束标记:最后一个参数必须是 DBUS_TYPE_INVALID
  • 错误处理:必须初始化 DBusError,失败时必须释放

内存管理说明

  • 字符串指针:指向消息内部数据,不需要释放,但消息释放后无效
  • 数值类型:是值的副本,可以安全使用和修改
  • 数组指针:指向消息内部数据,不需要释放

完整示例

// 示例:读取多个不同类型的参数
const char *name;
int age;
dbus_bool_t active;
double salary;

DBusError error;
dbus_error_init(&error);

if (dbus_message_get_args(
        msg, &error,
        DBUS_TYPE_STRING, &name,      // 参数1:字符串
        DBUS_TYPE_INT32, &age,        // 参数2:整数
        DBUS_TYPE_BOOLEAN, &active,   // 参数3:布尔值
        DBUS_TYPE_DOUBLE, &salary,    // 参数4:浮点数
        DBUS_TYPE_INVALID)) {
    // 成功读取所有参数
    printf("Name: %s\n", name);
    printf("Age: %d\n", age);
    printf("Active: %s\n", active ? "yes" : "no");
    printf("Salary: %.2f\n", salary);
    
    // 注意:name 指向消息内部,不要释放
    // age、active、salary 是值的副本,可以安全使用
} else {
    // 读取失败
    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Failed to get args: %s\n", error.message);
        dbus_error_free(&error);
    }
    return;
}
3.1.3 读取数组

固定长度类型数组

// 声明变量
int array_len;                    // 数组长度(输出)
const dbus_int32_t *array;        // 数组指针(输出,指向消息内部)

DBusError error;
dbus_error_init(&error);

// 读取数组参数
// 参数格式:DBUS_TYPE_ARRAY, 元素类型, 数组指针地址, 长度指针地址
if (dbus_message_get_args(
        msg, &error,
        DBUS_TYPE_ARRAY,                    // 数组类型标识
        DBUS_TYPE_INT32,                    // 元素类型:32位整数
        &array,                             // 数组指针的地址(输出)
        &array_len,                         // 数组长度的地址(输出)
        DBUS_TYPE_INVALID)) {
    // 读取成功,遍历数组
    printf("Array length: %d\n", array_len);
    for (int i = 0; i < array_len; i++) {
        printf("Array[%d] = %d\n", i, array[i]);
    }
    
    // 注意:
    // - array 指向消息内部数据,不需要释放
    // - 消息释放后 array 无效
    // - array_len 是值的副本,可以安全使用
} else {
    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error: %s\n", error.message);
        dbus_error_free(&error);
    }
}

字符串数组

// 声明变量
int array_len;                    // 数组长度(输出)
const char **array;               // 字符串数组指针(输出,需要释放)

DBusError error;
dbus_error_init(&error);

// 读取字符串数组
if (dbus_message_get_args(
        msg, &error,
        DBUS_TYPE_ARRAY,                    // 数组类型
        DBUS_TYPE_STRING,                   // 元素类型:字符串
        &array,                             // 字符串数组指针的地址(输出)
        &array_len,                         // 数组长度的地址(输出)
        DBUS_TYPE_INVALID)) {
    // 读取成功,遍历字符串数组
    printf("String array length: %d\n", array_len);
    for (int i = 0; i < array_len; i++) {
        printf("Array[%d] = %s\n", i, array[i]);
    }
    
    // 重要:字符串数组需要释放
    // 注意:array 本身需要释放,但 array[i] 指向消息内部,不需要释放
    dbus_free_string_array(array);
} else {
    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error: %s\n", error.message);
        dbus_error_free(&error);
    }
}

字符串数组内存管理说明

  • 数组指针array 是函数分配的内存,需要调用 dbus_free_string_array() 释放
  • 字符串指针array[i] 指向消息内部数据,不需要单独释放
  • 释放时机:使用完数组后立即释放,或在消息释放前释放

完整示例

// 示例1:读取整数数组
int ids_len;
const dbus_uint32_t *ids;

if (dbus_message_get_args(
        msg, NULL,  // 不使用错误对象
        DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &ids, &ids_len,
        DBUS_TYPE_INVALID)) {
    for (int i = 0; i < ids_len; i++) {
        printf("ID[%d] = %u\n", i, ids[i]);
    }
    // ids 指向消息内部,不需要释放
}

// 示例2:读取字符串数组
int names_len;
const char **names;

DBusError error;
dbus_error_init(&error);

if (dbus_message_get_args(
        msg, &error,
        DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &names, &names_len,
        DBUS_TYPE_INVALID)) {
    for (int i = 0; i < names_len; i++) {
        printf("Name[%d] = %s\n", i, names[i]);
    }
    // 必须释放 names
    dbus_free_string_array(names);
} else {
    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error: %s\n", error.message);
        dbus_error_free(&error);
    }
}

// 示例3:混合参数(数组 + 基本类型)
int count;
const dbus_int32_t *numbers;
const char *status;

if (dbus_message_get_args(
        msg, NULL,
        DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &numbers, &count,  // 数组参数
        DBUS_TYPE_STRING, &status,                            // 字符串参数
        DBUS_TYPE_INVALID)) {
    printf("Status: %s\n", status);
    printf("Numbers count: %d\n", count);
    for (int i = 0; i < count; i++) {
        printf("Number[%d] = %d\n", i, numbers[i]);
    }
}
3.1.4 实现细节

内部实现流程

dbus_message_get_args() 内部使用可变参数列表和消息迭代器实现:

dbus_bool_t dbus_message_get_args(DBusMessage *message,
                                  DBusError *error,
                                  int first_arg_type,
                                  ...)
{
  dbus_bool_t retval;
  va_list var_args;
  
  // 步骤 1: 参数验证
  _dbus_return_val_if_fail(message != NULL, FALSE);
  _dbus_return_val_if_error_is_set(error, FALSE);  // 如果 error 已设置,直接返回
  
  // 步骤 2: 初始化可变参数列表
  va_start(var_args, first_arg_type);
  
  // 步骤 3: 调用 valist 版本
  retval = dbus_message_get_args_valist(message, error, first_arg_type, var_args);
  
  // 步骤 4: 清理可变参数列表
  va_end(var_args);
  
  return retval;
}

核心实现(valist 版本)

dbus_bool_t dbus_message_get_args_valist(DBusMessage *message,
                                         DBusError *error,
                                         int first_arg_type,
                                         va_list var_args)
{
  DBusMessageIter iter;
  
  // 步骤 1: 初始化读取迭代器
  // 如果消息没有参数,返回 FALSE
  dbus_message_iter_init(message, &iter);
  
  // 步骤 2: 调用迭代器版本的参数获取函数
  return _dbus_message_iter_get_args_valist(&iter, error, first_arg_type, var_args);
}

迭代器参数获取实现

dbus_bool_t _dbus_message_iter_get_args_valist(DBusMessageIter *iter,
                                               DBusError *error,
                                               int first_arg_type,
                                               va_list var_args)
{
  DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
  int spec_type, msg_type, i;
  dbus_bool_t retval;
  va_list copy_args;
  
  // 步骤 1: 验证迭代器有效性
  _dbus_assert(_dbus_message_iter_check(real));
  
  retval = FALSE;
  spec_type = first_arg_type;
  i = 0;
  
  // 步骤 2: 复制可变参数列表(用于错误处理时的清理)
  va_copy(copy_args, var_args);
  
  // 步骤 3: 遍历参数列表
  while (spec_type != DBUS_TYPE_INVALID) {
    // 步骤 3.1: 获取消息中当前参数的实际类型
    msg_type = dbus_message_iter_get_arg_type(iter);
    
    // 步骤 3.2: 验证类型匹配
    if (msg_type != spec_type) {
      // 类型不匹配,设置错误信息
      dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
                    "Argument %d is specified to be of type \"%s\", but "
                    "is actually of type \"%s\"\n",
                    i,
                    _dbus_type_to_string(spec_type),
                    _dbus_type_to_string(msg_type));
      goto out;
    }
    
    // 步骤 3.3: 根据类型读取值
    if (spec_type == DBUS_TYPE_UNIX_FD) {
      // 情况 A: Unix 文件描述符
#ifdef HAVE_UNIX_FD_PASSING
      DBusBasicValue idx;
      int *pfd, nfd;
      
      // 获取存储 fd 的指针
      pfd = va_arg(var_args, int*);
      _dbus_assert(pfd);
      
      // 读取 fd 索引(消息体中存储的是索引,不是实际的 fd)
      _dbus_type_reader_read_basic(&real->u.reader, &idx);
      
      // 验证索引有效性
      if (idx.u32 >= real->message->n_unix_fds) {
        dbus_set_error(error, DBUS_ERROR_INCONSISTENT_MESSAGE,
                      "Message refers to file descriptor at index %i, "
                      "but has only %i descriptors attached.\n",
                      idx.u32, real->message->n_unix_fds);
        goto out;
      }
      
      // dup() 文件描述符(调用者负责关闭)
      if ((nfd = _dbus_dup(real->message->unix_fds[idx.u32], error)) < 0)
        goto out;
      
      *pfd = nfd;
#else
      dbus_set_error(error, DBUS_ERROR_NOT_SUPPORTED,
                    "Platform does not support file descriptor passing.\n");
      goto out;
#endif
    }
    else if (dbus_type_is_basic(spec_type)) {
      // 情况 B: 基本类型(整数、字符串、布尔值等)
      void *ptr;
      
      // 获取存储值的指针
      ptr = va_arg(var_args, void *);
      _dbus_assert(ptr != NULL);
      
      // 从消息体中读取基本类型值
      // 注意:字符串指针指向消息内部数据,不需要释放
      _dbus_type_reader_read_basic(&real->u.reader, ptr);
    }
    else if (spec_type == DBUS_TYPE_ARRAY) {
      // 情况 C: 数组类型
      int element_type;
      int spec_element_type;
      const void **ptr;
      int *n_elements_p;
      DBusTypeReader array;
      
      // 获取期望的元素类型
      spec_element_type = va_arg(var_args, int);
      
      // 获取消息中数组的实际元素类型
      element_type = _dbus_type_reader_get_element_type(&real->u.reader);
      
      // 验证元素类型匹配
      if (spec_element_type != element_type) {
        dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
                      "Argument %d is specified to be an array of \"%s\", but "
                      "is actually an array of \"%s\"\n",
                      i,
                      _dbus_type_to_string(spec_element_type),
                      _dbus_type_to_string(element_type));
        goto out;
      }
      
      if (dbus_type_is_fixed(spec_element_type) &&
          element_type != DBUS_TYPE_UNIX_FD) {
        // 情况 C1: 固定长度类型数组
        ptr = va_arg(var_args, const void **);
        n_elements_p = va_arg(var_args, int*);
        
        _dbus_assert(ptr != NULL);
        _dbus_assert(n_elements_p != NULL);
        
        // 进入数组,读取整个数组
        _dbus_type_reader_recurse(&real->u.reader, &array);
        _dbus_type_reader_get_fixed_array(&array, element_type,
                                         ptr, n_elements_p);
        // 注意:数组指针指向消息内部数据,不需要释放
      }
      else if (_DBUS_TYPE_IS_STRINGLIKE(element_type)) {
        // 情况 C2: 字符串类数组
        const char ***ptr_p;
        const char **ptr;
        int *n_elements_p;
        DBusTypeReader array;
        int j;
        
        ptr_p = va_arg(var_args, const char***);
        n_elements_p = va_arg(var_args, int*);
        
        // 进入数组
        _dbus_type_reader_recurse(&real->u.reader, &array);
        
        // 计算数组元素数量
        j = 0;
        while (_dbus_type_reader_get_current_type(&array) != DBUS_TYPE_INVALID) {
          j++;
          _dbus_type_reader_next(&array);
        }
        
        *n_elements_p = j;
        
        // 分配字符串数组(需要调用者释放)
        ptr = dbus_new(const char*, j);
        if (ptr == NULL) {
          _DBUS_SET_OOM(error);
          goto out;
        }
        
        // 重新进入数组,读取每个字符串
        _dbus_type_reader_recurse(&real->u.reader, &array);
        j = 0;
        while (_dbus_type_reader_get_current_type(&array) != DBUS_TYPE_INVALID) {
          _dbus_type_reader_read_basic(&array, &ptr[j]);
          _dbus_type_reader_next(&array);
          j++;
        }
        
        *ptr_p = ptr;
      }
      else {
        // 情况 C3: 不支持的数组类型
        dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
                      "Arrays of %s are not supported by dbus_message_get_args()",
                      _dbus_type_to_string(element_type));
        goto out;
      }
    }
    
    // 步骤 3.4: 移动到下一个参数
    dbus_message_iter_next(iter);
    spec_type = va_arg(var_args, int);
    i++;
  }
  
  retval = TRUE;
  
out:
  // 错误处理:如果失败,需要清理已分配的资源
  // (如字符串数组)
  va_end(copy_args);
  return retval;
}

关键实现细节说明

  1. 类型验证

    • 每个参数都会验证类型是否匹配
    • 类型不匹配会设置详细的错误信息
    • 错误信息包含参数索引和期望/实际类型
  2. 基本类型读取

    • 使用 _dbus_type_reader_read_basic() 读取值
    • 字符串指针指向消息内部数据,不需要释放
    • 数值类型直接复制到提供的地址
  3. 数组读取

    • 固定长度类型:使用 get_fixed_array() 一次性读取
    • 字符串数组:需要分配内存存储字符串指针数组
    • 复杂类型:不支持,需要使用迭代器手动处理
  4. Unix 文件描述符处理

    • 消息体中存储的是 fd 索引,不是实际的 fd
    • 读取时会 dup() 文件描述符
    • 调用者负责关闭返回的 fd
  5. 错误处理

    • 任何步骤失败都会设置错误信息
    • 已分配的资源会被正确清理
    • 返回 FALSE 表示失败
  6. 内存管理

    • 基本类型值直接复制到提供的地址
    • 字符串指针指向消息内部,不需要释放
    • 字符串数组需要调用者释放(使用 dbus_free_string_array()
    • 固定长度类型数组指针指向消息内部,不需要释放

3.2 使用消息迭代器

3.2.1 初始化读取迭代器
DBusMessageIter iter;

// 初始化迭代器用于读取模式
// 返回值:TRUE 表示消息有参数,FALSE 表示消息没有参数
if (!dbus_message_iter_init(message, &iter)) {
    // 消息没有参数,可以直接返回
    printf("Message has no arguments\n");
    return;
}

// 迭代器已初始化,指向第一个参数
// 注意:
// - 迭代器分配在栈上,不需要手动释放
// - 迭代器指向消息体中的第一个参数
// - 可以安全地复制迭代器(通过赋值)

初始化说明

  • 读取模式:迭代器被设置为 DBUS_MESSAGE_ITER_TYPE_READER
  • 位置:迭代器指向消息体的第一个参数
  • 字节序:如果消息字节序与编译器不同,会自动转换
  • 签名获取:从消息头获取类型签名,用于验证参数类型
3.2.2 读取基本类型

基本类型读取流程

DBusMessageIter iter;
if (!dbus_message_iter_init(message, &iter)) {
    return;  // 消息没有参数
}

// 步骤 1: 获取当前参数的类型
int type = dbus_message_iter_get_arg_type(&iter);

// 步骤 2: 根据类型读取值
if (type == DBUS_TYPE_STRING) {
    // 情况 A: 字符串类型
    const char *str;
    
    // 读取字符串值
    // 注意:传递 str 的地址,函数会将字符串指针写入
    dbus_message_iter_get_basic(&iter, &str);
    
    // 使用字符串
    printf("String: %s\n", str);
    
    // 注意:str 指向消息内部数据,不需要释放
    // 消息释放后 str 无效
}
else if (type == DBUS_TYPE_INT32) {
    // 情况 B: 整数类型
    dbus_int32_t num;
    
    // 读取整数值
    dbus_message_iter_get_basic(&iter, &num);
    
    printf("Integer: %d\n", num);
    // num 是值的副本,可以安全使用和修改
}
else if (type == DBUS_TYPE_BOOLEAN) {
    // 情况 C: 布尔类型
    dbus_bool_t flag;
    
    dbus_message_iter_get_basic(&iter, &flag);
    
    printf("Boolean: %s\n", flag ? "true" : "false");
}

// 步骤 3: 移动到下一个参数
// 注意:读取完当前参数后,必须调用 next() 移动到下一个参数
dbus_message_iter_next(&iter);

完整遍历示例

DBusMessageIter iter;
if (!dbus_message_iter_init(message, &iter)) {
    printf("Message has no arguments\n");
    return;
}

// 遍历所有参数
int arg_index = 0;
do {
    int type = dbus_message_iter_get_arg_type(&iter);
    
    if (type == DBUS_TYPE_INVALID) {
        break;  // 没有更多参数
    }
    
    printf("Argument %d: type = %c\n", arg_index, type);
    
    // 根据类型读取值
    switch (type) {
        case DBUS_TYPE_STRING: {
            const char *str;
            dbus_message_iter_get_basic(&iter, &str);
            printf("  Value: %s\n", str);
            break;
        }
        case DBUS_TYPE_INT32: {
            dbus_int32_t num;
            dbus_message_iter_get_basic(&iter, &num);
            printf("  Value: %d\n", num);
            break;
        }
        case DBUS_TYPE_BOOLEAN: {
            dbus_bool_t flag;
            dbus_message_iter_get_basic(&iter, &flag);
            printf("  Value: %s\n", flag ? "true" : "false");
            break;
        }
        case DBUS_TYPE_DOUBLE: {
            double value;
            dbus_message_iter_get_basic(&iter, &value);
            printf("  Value: %f\n", value);
            break;
        }
        default:
            printf("  Value: (unsupported type)\n");
            break;
    }
    
    arg_index++;
} while (dbus_message_iter_next(&iter));

printf("Total arguments: %d\n", arg_index);

关键点说明

  1. 类型检查:读取前应该检查类型,避免类型不匹配
  2. 地址传递get_basic() 需要传递存储地址(&variable
  3. 移动到下一个:读取完参数后必须调用 next() 移动到下一个参数
  4. 内存管理:字符串指针指向消息内部,不需要释放
  5. 迭代器状态:迭代器可以安全复制,但修改消息会使迭代器失效
3.2.3 读取数组

基本数组读取

DBusMessageIter iter;
if (!dbus_message_iter_init(message, &iter)) {
    return;
}

// 步骤 1: 检查当前参数是否为数组
int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_ARRAY) {
    DBusMessageIter sub;
    
    // 步骤 2: 获取数组元素类型
    int element_type = dbus_message_iter_get_element_type(&iter);
    printf("Array element type: %c\n", element_type);
    
    // 步骤 3: 进入数组(创建子迭代器)
    // recurse() 会创建一个新的迭代器,指向数组的第一个元素
    dbus_message_iter_recurse(&iter, &sub);
    
    // 步骤 4: 遍历数组元素
    int element_index = 0;
    while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
        // 检查元素类型(应该与 element_type 匹配)
        int current_type = dbus_message_iter_get_arg_type(&sub);
        
        if (current_type == DBUS_TYPE_INT32) {
            // 读取整数元素
            dbus_int32_t value;
            dbus_message_iter_get_basic(&sub, &value);
            printf("Array[%d] = %d\n", element_index, value);
        }
        else if (current_type == DBUS_TYPE_STRING) {
            // 读取字符串元素
            const char *str;
            dbus_message_iter_get_basic(&sub, &str);
            printf("Array[%d] = %s\n", element_index, str);
        }
        
        // 移动到下一个元素
        dbus_message_iter_next(&sub);
        element_index++;
    }
    
    printf("Array has %d elements\n", element_index);
    
    // 步骤 5: 移动到下一个参数(退出数组)
    dbus_message_iter_next(&iter);
}

固定长度类型数组优化

// 对于固定长度类型数组,可以使用 get_fixed_array() 一次性读取
int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_ARRAY) {
    int element_type = dbus_message_iter_get_element_type(&iter);
    
    if (element_type == DBUS_TYPE_INT32) {
        // 使用固定数组读取(更高效)
        const dbus_int32_t *array;
        int n_elements;
        
        // 一次性读取整个数组
        dbus_message_iter_get_fixed_array(&iter, &array, &n_elements);
        
        printf("Array length: %d\n", n_elements);
        for (int i = 0; i < n_elements; i++) {
            printf("Array[%d] = %d\n", i, array[i]);
        }
        
        // 注意:array 指向消息内部,不需要释放
    }
    
    dbus_message_iter_next(&iter);
}

字符串数组读取

int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_ARRAY) {
    int element_type = dbus_message_iter_get_element_type(&iter);
    
    if (element_type == DBUS_TYPE_STRING) {
        DBusMessageIter sub;
        dbus_message_iter_recurse(&iter, &sub);
        
        // 先计算数组长度
        int count = 0;
        DBusMessageIter temp = sub;  // 复制迭代器用于计数
        while (dbus_message_iter_get_arg_type(&temp) != DBUS_TYPE_INVALID) {
            count++;
            dbus_message_iter_next(&temp);
        }
        
        printf("String array length: %d\n", count);
        
        // 遍历字符串数组
        int i = 0;
        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
            const char *str;
            dbus_message_iter_get_basic(&sub, &str);
            printf("Array[%d] = %s\n", i, str);
            // 注意:str 指向消息内部,不需要释放
            dbus_message_iter_next(&sub);
            i++;
        }
    }
    
    dbus_message_iter_next(&iter);
}

嵌套数组读取

// 读取数组的数组(如 int32[][])
int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_ARRAY) {
    int element_type = dbus_message_iter_get_element_type(&iter);
    
    if (element_type == DBUS_TYPE_ARRAY) {
        // 外层数组:数组的数组
        DBusMessageIter outer;
        dbus_message_iter_recurse(&iter, &outer);
        
        int row = 0;
        while (dbus_message_iter_get_arg_type(&outer) != DBUS_TYPE_INVALID) {
            // 内层数组:整数数组
            int inner_element_type = dbus_message_iter_get_element_type(&outer);
            if (inner_element_type == DBUS_TYPE_INT32) {
                DBusMessageIter inner;
                dbus_message_iter_recurse(&outer, &inner);
                
                printf("Row %d: ", row);
                while (dbus_message_iter_get_arg_type(&inner) != DBUS_TYPE_INVALID) {
                    dbus_int32_t value;
                    dbus_message_iter_get_basic(&inner, &value);
                    printf("%d ", value);
                    dbus_message_iter_next(&inner);
                }
                printf("\n");
            }
            
            dbus_message_iter_next(&outer);
            row++;
        }
    }
    
    dbus_message_iter_next(&iter);
}

关键点说明

  1. recurse() 创建子迭代器:进入数组时会创建新的迭代器,不影响父迭代器
  2. 元素类型检查:使用 get_element_type() 获取数组元素类型
  3. 固定数组优化:固定长度类型数组可以使用 get_fixed_array() 一次性读取
  4. 遍历模式:使用 while 循环遍历,直到类型为 DBUS_TYPE_INVALID
  5. 退出数组:遍历完数组后,必须调用父迭代器的 next() 移动到下一个参数
3.2.4 读取结构体

基本结构体读取

DBusMessageIter iter;
if (!dbus_message_iter_init(message, &iter)) {
    return;
}

// 步骤 1: 检查当前参数是否为结构体
int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_STRUCT) {
    DBusMessageIter sub;
    
    // 步骤 2: 进入结构体(创建子迭代器)
    // recurse() 会创建一个新的迭代器,指向结构体的第一个字段
    dbus_message_iter_recurse(&iter, &sub);
    
    // 步骤 3: 按顺序读取结构体字段
    // 注意:结构体字段必须按顺序读取,不能跳过
    
    // 字段1:字符串(姓名)
    int field1_type = dbus_message_iter_get_arg_type(&sub);
    if (field1_type == DBUS_TYPE_STRING) {
        const char *name;
        dbus_message_iter_get_basic(&sub, &name);
        printf("Name: %s\n", name);
        // 注意:name 指向消息内部,不需要释放
    }
    dbus_message_iter_next(&sub);  // 移动到下一个字段
    
    // 字段2:整数(年龄)
    int field2_type = dbus_message_iter_get_arg_type(&sub);
    if (field2_type == DBUS_TYPE_INT32) {
        dbus_int32_t age;
        dbus_message_iter_get_basic(&sub, &age);
        printf("Age: %d\n", age);
    }
    dbus_message_iter_next(&sub);  // 移动到下一个字段(如果有)
    
    // 字段3:布尔值(是否激活)
    int field3_type = dbus_message_iter_get_arg_type(&sub);
    if (field3_type == DBUS_TYPE_BOOLEAN) {
        dbus_bool_t active;
        dbus_message_iter_get_basic(&sub, &active);
        printf("Active: %s\n", active ? "yes" : "no");
    }
    // 如果还有更多字段,继续读取...
    
    // 步骤 4: 移动到下一个参数(退出结构体)
    dbus_message_iter_next(&iter);
}

通用结构体读取函数

// 辅助函数:读取结构体 (string, int32, boolean)
dbus_bool_t read_person_struct(DBusMessageIter *iter,
                               const char **name,
                               int *age,
                               dbus_bool_t *active)
{
    DBusMessageIter sub;
    
    // 检查类型
    if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
        return FALSE;
    }
    
    // 进入结构体
    dbus_message_iter_recurse(iter, &sub);
    
    // 读取字段1:姓名
    if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
        return FALSE;
    }
    dbus_message_iter_get_basic(&sub, name);
    dbus_message_iter_next(&sub);
    
    // 读取字段2:年龄
    if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INT32) {
        return FALSE;
    }
    dbus_message_iter_get_basic(&sub, age);
    dbus_message_iter_next(&sub);
    
    // 读取字段3:激活状态
    if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) {
        return FALSE;
    }
    dbus_message_iter_get_basic(&sub, active);
    
    return TRUE;
}

// 使用示例
DBusMessageIter iter;
if (dbus_message_iter_init(message, &iter)) {
    const char *name;
    int age;
    dbus_bool_t active;
    
    if (read_person_struct(&iter, &name, &age, &active)) {
        printf("Person: %s, %d years old, %s\n",
               name, age, active ? "active" : "inactive");
    }
    
    dbus_message_iter_next(&iter);
}

嵌套结构体读取

// 结构体:(string, (int32, string))
// 表示:姓名、地址(邮编、街道)

int type = dbus_message_iter_get_arg_type(&iter);
if (type == DBUS_TYPE_STRUCT) {
    DBusMessageIter outer;
    dbus_message_iter_recurse(&iter, &outer);
    
    // 字段1:姓名(字符串)
    const char *name;
    dbus_message_iter_get_basic(&outer, &name);
    printf("Name: %s\n", name);
    dbus_message_iter_next(&outer);
    
    // 字段2:地址(嵌套结构体)
    int address_type = dbus_message_iter_get_arg_type(&outer);
    if (address_type == DBUS_TYPE_STRUCT) {
        DBusMessageIter inner;
        dbus_message_iter_recurse(&outer, &inner);
        
        // 地址字段1:邮编(整数)
        dbus_int32_t zipcode;
        dbus_message_iter_get_basic(&inner, &zipcode);
        printf("Zipcode: %d\n", zipcode);
        dbus_message_iter_next(&inner);
        
        // 地址字段2:街道(字符串)
        const char *street;
        dbus_message_iter_get_basic(&inner, &street);
        printf("Street: %s\n", street);
    }
    
    dbus_message_iter_next(&iter);
}

关键点说明

  1. 字段顺序:结构体字段必须按顺序读取,不能跳过中间字段
  2. 类型检查:读取前应该检查字段类型,避免类型不匹配
  3. 子迭代器recurse() 创建子迭代器,不影响父迭代器
  4. 字段遍历:使用 next() 移动到下一个字段
  5. 退出结构体:读取完所有字段后,调用父迭代器的 next() 移动到下一个参数
  6. 嵌套支持:结构体可以嵌套,形成复杂的层次结构

4. 参数类型和签名

4.1 D-Bus 类型系统

D-Bus 支持以下基本类型:

类型代码类型名C 类型说明
'y'BYTEdbus_uint8_t8 位无符号整数
'b'BOOLEANdbus_bool_t布尔值(4 字节)
'n'INT16dbus_int16_t16 位有符号整数
'q'UINT16dbus_uint16_t16 位无符号整数
'i'INT32dbus_int32_t32 位有符号整数
'u'UINT32dbus_uint32_t32 位无符号整数
'x'INT64dbus_int64_t64 位有符号整数
't'UINT64dbus_uint64_t64 位无符号整数
'd'DOUBLEdouble双精度浮点数
's'STRINGconst char*UTF-8 字符串
'o'OBJECT_PATHconst char*对象路径
'g'SIGNATUREconst char*类型签名
'h'UNIX_FDintUnix 文件描述符

4.2 容器类型

类型代码类型名说明
'a'ARRAY数组
'r'STRUCT结构体(使用 () 表示)
'e'DICT_ENTRY字典条目(使用 {} 表示)
'v'VARIANT变体类型

4.3 类型签名

类型签名是描述参数类型的字符串:

示例

  • "s":单个字符串
  • "is":整数 + 字符串
  • "a{sv}":字典(字符串键,Variant 值)
  • "(is)":结构体(整数 + 字符串)
  • "a(is)":结构体数组

4.4 获取消息签名

const char *signature = dbus_message_get_signature(message);
if (signature != NULL) {
    printf("Message signature: %s\n", signature);
}

注意:签名字符串指向消息内部数据,不需要释放。

5. 参数验证

5.1 类型验证

dbus_message_get_args() 会自动验证参数类型:

// 如果类型不匹配,会设置错误
if (!dbus_message_get_args(
        msg, &error,
        DBUS_TYPE_STRING, &str,  // 期望字符串
        DBUS_TYPE_INVALID)) {
    // 如果消息的第一个参数不是字符串,会失败
    fprintf(stderr, "Type mismatch: %s\n", error.message);
}

5.2 数量验证

// 如果参数数量不匹配,迭代器会返回 DBUS_TYPE_INVALID
DBusMessageIter iter;
dbus_message_iter_init(message, &iter);

int count = 0;
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
    count++;
    dbus_message_iter_next(&iter);
}

printf("Message has %d arguments\n", count);

5.3 字符串验证

添加字符串时会自动验证 UTF-8 编码:

// 无效的 UTF-8 字符串会导致失败
const char *invalid_utf8 = "\xFF\xFE";  // 无效 UTF-8
if (!dbus_message_append_args(
        msg,
        DBUS_TYPE_STRING, &invalid_utf8,
        DBUS_TYPE_INVALID)) {
    // 会失败
}

6. 最佳实践

6.1 错误处理

DBusError error;
dbus_error_init(&error);

if (!dbus_message_get_args(msg, &error, ...)) {
    fprintf(stderr, "Failed to get args: %s\n", error.message);
    dbus_error_free(&error);
    return;
}

6.2 参数数量检查

// 检查参数数量
const char *signature = dbus_message_get_signature(msg);
int expected_count = strlen(signature);  // 简化示例

DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
int actual_count = 0;
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
    actual_count++;
    dbus_message_iter_next(&iter);
}

if (actual_count != expected_count) {
    fprintf(stderr, "Argument count mismatch\n");
    return;
}

6.3 类型安全

// 使用迭代器时检查类型
int type = dbus_message_iter_get_arg_type(&iter);
if (type != DBUS_TYPE_STRING) {
    fprintf(stderr, "Expected string, got type %c\n", type);
    return;
}

6.4 内存管理

// 字符串指针指向消息内部,不需要释放
const char *str;
dbus_message_get_args(msg, NULL,
                     DBUS_TYPE_STRING, &str,
                     DBUS_TYPE_INVALID);
// str 指向消息内部数据,消息释放后无效

// 数组指针也指向消息内部
const dbus_int32_t *array;
int array_len;
dbus_message_get_args(msg, NULL,
                     DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &array, &array_len,
                     DBUS_TYPE_INVALID);
// array 指向消息内部数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值