消息(方法调用)创建后的典型流程
仅仅创建消息头是不够的,完整的调用流程如下:
-
创建方法调用消息 -
dbus_message_new_method_call -
添加参数 -
dbus_message_append_args -
发送消息 -
dbus_connection_send -
(可选)等待并获取回复 -
dbus_connection_send_with_reply_and_block
同系列类似函数的对比
为了更好地理解,这里有几个相关的函数:
| 函数 | 用途 | 类比 |
|---|---|---|
dbus_message_new_method_call |
创建方法调用(客户端) | 写一封请求信:"请执行X方法" |
dbus_message_new_signal |
创建信号消息 | 发一个广播通知:"某事件发生了" |
dbus_message_new_method_return |
创建方法回复(服务端) | 回信:"方法执行结果是这样" |
dbus_message_new_error |
创建错误回复(服务端) | 回信:"执行出错了" |
使用示例
让我们通过几个具体例子来理解:
示例1:调用一个无参数的方法
// 让VLC播放器开始播放
DBusMessage *message = dbus_message_new_method_call("org.mpris.MediaPlayer2.vlc", // 目标服务"/org/mpris/MediaPlayer2", // 对象路径"org.mpris.MediaPlayer2.Player", // 接口"Play" // 方法名
);if (message == NULL) {fprintf(stderr, "创建消息失败:内存不足\n");return;
}// 发送消息(不等待回复)
dbus_connection_send(connection, message, NULL);
dbus_message_unref(message); // 重要:释放消息
示例2:调用带参数的方法
// 调用计算器服务的加法方法:Add(10, 20)
DBusMessage *message = dbus_message_new_method_call("com.example.Calculator","/com/example/Calculator","com.example.Calculator.Basic","Add"
);if (message == NULL) {fprintf(stderr, "创建消息失败\n");return;
}// 添加参数
dbus_uint32_t a = 10, b = 20;
if (!dbus_message_append_args(message,DBUS_TYPE_UINT32, &a,DBUS_TYPE_UINT32, &b,DBUS_TYPE_INVALID)) {fprintf(stderr, "添加参数失败\n");dbus_message_unref(message);return;
}// 发送并等待回复
DBusError error;
dbus_error_init(&error);
DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, message, 1000, &timeout, &error // 1秒超时
);if (dbus_error_is_set(&error)) {fprintf(stderr, "调用失败: %s\n", error.message);dbus_error_free(&error);
} else if (reply) {// 处理回复...dbus_message_unref(reply);
}dbus_message_unref(message);
示例3:调用设置属性值的方法
// 设置音量:SetVolume(0.8)
DBusMessage *message = dbus_message_new_method_call("org.mpris.MediaPlayer2.vlc","/org/mpris/MediaPlayer2","org.freedesktop.DBus.Properties","Set" // 标准的Properties接口的Set方法
);if (message) {const char *interface = "org.mpris.MediaPlayer2.Player";const char *property_name = "Volume";double volume = 0.8;DBusMessageIter iter, sub;dbus_message_iter_init_append(message, &iter);// 参数是:接口名,属性名,属性值dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface);dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property_name);// 属性值需要包装为variant类型dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "d", &sub);dbus_message_iter_append_basic(&sub, DBUS_TYPE_DOUBLE, &volume);dbus_message_iter_close_container(&iter, &sub);// 发送消息...dbus_connection_send(connection, message, NULL);dbus_message_unref(message);
}
核心概念
dbus_message_new_method_call 是用于创建一个D-Bus方法调用消息的函数。简单来说,它就是用来构造一个"请求包",让你能够调用另一个D-Bus服务提供的方法。
可以把D-Bus通信想象成远程过程调用(RPC):
-
服务端:提供各种方法(比如
Play,Pause,GetVolume) -
客户端:需要调用这些方法
-
dbus_message_new_method_call:就是客户端用来"写调用请求"的工具
函数原型
DBusMessage * dbus_message_new_method_call(const char *service,const char *path,const char *interface,const char *method);
参数详解
这四个参数共同确定了"要调用谁的方法":
-
service(const char *)-
目标服务的众所周知名称。这就是对方通过
dbus_bus_request_name注册的名称。 -
示例:
"org.mpris.MediaPlayer2.vlc","com.example.CalculatorService"
-
-
path(const char *)-
对象路径。在D-Bus中,一个服务可以暴露多个对象,每个对象有唯一的路径,类似于文件系统路径。
-
示例:
"/org/mpris/MediaPlayer2","/com/example/Calculator" -
通常以反向DNS格式组织。
-
-
interface(const char *)-
接口名称。一个对象可以实现多个接口,每个接口包含一组相关的方法和信号。
-
示例:
"org.mpris.MediaPlayer2.Player","com.example.Calculator.Basic" -
这相当于编程中的接口或类名。
-
-
method(const char *)-
要调用的具体方法名。
-
示例:
"Play","Pause","Add","GetProperty"
-
返回值
-
成功:返回一个指向新创建的
DBusMessage对象的指针。 -
失败:返回
NULL(通常是因为内存不足)。
重要:返回的消息对象已经过引用计数,使用完后必须用 dbus_message_unref 释放。