DBus类型系统简介
DBus类型系统简介
DBus是一个跨平台的消息总线系统,旨在为不同程序之间提供一种简单有效的通信机制。它支持Linux、Windows和其他UNIX系统上的多种应用程序,常用于桌面环境、硬件设备驱动和系统服务。DBus类型系统定义了一组数据类型,使得消息能够通过总线进行传输,并且允许应用程序之间以结构化的方式共享数据。
以下是DBus类型系统的基本数据类型:
- 基本类型:
- 字节 (Byte):表示一个8位无符号整数,用大写字母
y
表示。 - 布尔 (Boolean):表示真(True)或假(False),用大写字母
b
表示。 - 有符号整数 (Signed Integer):有三种长度,分别为16位、32位和64位,分别用大写字母
s
,i
和x
表示。 - 无符号整数 (Unsigned Integer):有三种长度,分别为16位、32位和64位,分别用大写字母
q
,u
和t
表示。 - 双精度浮点数 (Double):表示一个64位双精度浮点数,用大写字母
d
表示。 - 字符串 (String):表示一个以NUL字符结尾的UTF-8字符串,用大写字母
s
表示。
- 复合类型:
- 数组 (Array):表示一个具有相同类型元素的连续集合,用大写字母
a
表示。数组类型后跟用于描述元素类型的字符,例如:as
表示字符串数组,au
表示无符号整数数组。 - 字典 (Dictionary):表示一个包含键值对的集合,用大写字母
a{}
表示。字典键必须是简单类型(不可为数组或字典),值可以是任何类型。例如:a{si}
表示以字符串为键、整数为值的字典,a{sv}
表示以字符串为键、任意类型为值的字典(v
表示变体类型)。 - 结构体 (Struct):表示一个有固定数量且类型已知的元素组成的结构,用一对括号
()
表示。例如:(is)
表示一个结构体,包含一个整数和一个字符串,(ias)
表示一个结构体,包含一个整数和一个字符串数组。 - 变体 (Variant):表示一个动态类型,可以包含任意DBus类型的数据,用大写字母
v
表示。
DBus类型系统为程序员提供了一种灵活的方式来构建和解析消息,进而支持各种应用程序之间的通信。同时,类型系统的设计使得DBus在不同平台和语言环境下能够高效地进行数据交换,提高了开发者的生产力。
QtDBus 是 Qt 库中的一个模块,用于在 Qt 应用程序中使用 D-Bus IPC (进程间通信) 机制。D-Bus 是一种通用的进程间通信框架,让各个独立的程序可以通过公共的消息总线进行通信。QtDBus 模块提供了对这个机制的封装,使得在 Qt 应用程序中使用 D-Bus 更加方便。
为了在 D-Bus 上通信,程序需要发送和接收数据。QtDBus 模块实现了 D-Bus 的扩展类型系统,支持多种数据类型,包括原生类型和复合类型。这些数据类型可以通过 QDBusArgument 类进行编码和解码。
下面是一些 QtDBus 模块支持的基本类型和复合类型:
基本类型:
- bool:布尔类型,用于表示真或假。
- qint8, qint16, qint32, qint64:分别表示 8 位、16 位、32 位和 64 位整数。
- quint8, quint16, quint32, quint64:分别表示 8 位、16 位、32 位和 64 位无符号整数。
- double:双精度浮点数。
- QString:表示字符串。
复合类型:
- QDBusObjectPath:表示 D-Bus 对象路径。
- QDBusSignature:表示 D-Bus 签名。
- QDBusVariant:表示可变类型,可以包含任何类型的值。
- QVariantList:表示 QVariant 类型的列表,可以存储不同类型的值。
- QStringList:表示字符串列表。
- QHash:表示一个哈希表,其中键为字符串,值为 QVariant 类型。
要在 D-Bus 总线上发送或接收这些类型的数据,可以使用 QDBusMessage 类。QDBusMessage 支持几种不同类型的消息,如方法调用、方法返回、信号和错误。通过 QDBusMessage,可以设置消息的参数和读取参数。
要将参数添加到 QDBusMessage,可以使用 QDBusArgument 类。QDBusArgument 类提供了一组操作符重载,用于编码和解码 QtDBus 支持的各种类型。这使得在 Qt 应用程序中使用 D-Bus 总线变得非常简单和直观。
总之,QtDBus 模块实现了 D-Bus 的扩展类型系统,使得在 Qt 应用程序中使用 D-Bus 进程间通信变得简单。QDBusArgument 类支持编码和解码多种数据类型,方便应用程序在总线上发送和接收数据。
原生类型(Primitive Types)
DBus 原始类型是构成 DBus 通信协议的基本数据类型。这些原始类型在 DBus 中用于表示方法参数、返回值、信号等数据。以下是对各种原始类型的文字描述:
- 字节(BYTE):无符号字节(8位)表示一个字节的数据,范围从 0 到 255。
- 布尔(BOOLEAN):布尔值表示真或假的逻辑值,通常用于表示条件、状态或者控制流程。
- 有符号16位整数(INT16):有符号 16 位整数表示一个范围为 -32,768 到 32,767 的整数。
- 无符号16位整数(UINT16):无符号 16 位整数表示一个范围为 0 到 65,535 的整数。
- 有符号32位整数(INT32):有符号 32 位整数表示一个范围为 -2,147,483,648 到 2,147,483,647 的整数。
- 无符号32位整数(UINT32):无符号 32 位整数表示一个范围为 0 到 4,294,967,295 的整数。
- 有符号64位整数(INT64):有符号 64 位整数表示一个范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 的整数。
- 无符号64位整数(UINT64):无符号 64 位整数表示一个范围为 0 到 18,446,744,073,709,551,615 的整数。
- 双精度浮点数(DOUBLE):双精度浮点数表示一个具有双精度(64 位)的实数,可以表示大范围的小数值。
- 字符串(STRING):字符串表示一个由字符组成的文本序列,采用 UTF-8 编码。字符串在 DBus 中可以用于表示文本、文件名、URL 等信息。
- 对象路径(OBJECT_PATH):对象路径是一个特殊类型的字符串,表示 DBus 服务中对象的路径。对象路径类似于 UNIX 文件路径,用于定位 DBus 上的特定对象。
- 类型签名(SIGNATURE):类型签名是一种元数据,用于描述数据的类型和结构。它在 DBus 中用于表示接口、方法和信号的参数类型。类型签名由一系列字符组成,每个字符表示一种数据类型。
这些原始类型在 DBus 中广泛使用,它们可以单独使用,也可以与复合类型结合使用,以满足各种复杂的数据传输需求。
DBus 数据类型 | Qt 数据类型 | C++ 标准库 (std) 数据类型 | 说明 |
字节(BYTE) | quint8 | uint8_t | 无符号字节(8位) |
布尔(BOOLEAN) | bool | bool | 布尔值(真或假) |
有符号16位整数(INT16) | qint16 | int16_t | 有符号 16 位整数 |
无符号16位整数(UINT16) | quint16 | uint16_t | 无符号 16 位整数 |
有符号32位整数(INT32) | qint32 | int32_t | 有符号 32 位整数 |
无符号32位整数(UINT32) | quint32 | uint32_t | 无符号 32 位整数 |
有符号64位整数(INT64) | qint64 | int64_t | 有符号 64 位整数 |
无符号64位整数(UINT64) | quint64 | uint64_t | 无符号 64 位整数 |
双精度浮点数(DOUBLE) | double | double | 双精度浮点数 |
字符串(STRING) | QString | std::string | 字符串(UTF-8 编码) |
对象路径(OBJECT_PATH) | QDBusObjectPath | 无对应类型 | 对象路径(类似于 UNIX 文件路径) |
类型签名(SIGNATURE) | QDBusSignature | 无对应类型 | 类型签名(一种元数据,描述数据的类型和结构) |
原生类型综合示例
在 DBus 中,我们主要用这些数据类型来定义接口、方法和信号。接下来我会通过一个简单的例子来说明如何在 DBus 中使用这些数据类型。
假设我们有一个计算器应用,它有一个接口 com.example.Calculator
,包含两个方法:Add
和 Multiply
。这个接口的定义如下:
<node> <interface name="com.example.Calculator"> <method name="Add"> <arg type="i" direction="in" name="a"/> <arg type="i" direction="in" name="b"/> <arg type="i" direction="out" name="result"/> </method> <method name="Multiply"> <arg type="i" direction="in" name="a"/> <arg type="i" direction="in" name="b"/> <arg type="i" direction="out" name="result"/> </method> </interface> </node>
在这个例子中,我们使用了 INT32
类型(在 XML 中表示为 i
),定义了两个输入参数 a
和 b
,以及一个输出参数 result
。Add
方法用于求和,Multiply
方法用于求积。
接下来,我们可以在客户端调用这些方法,例如在 Python 中使用 pydbus
库:
from pydbus import SystemBus bus = SystemBus() calculator = bus.get("com.example.Calculator", "/com/example/Calculator") a = 3 b = 4 sum_result = calculator.Add(a, b) product_result = calculator.Multiply(a, b) print(f"Sum: {sum_result}") print(f"Product: {product_result}")
在这个客户端示例中,我们首先通过 SystemBus
连接到 DBus 总线,然后获取 com.example.Calculator
服务的 /com/example/Calculator
对象路径。接着,我们调用 Add
和 Multiply
方法,并将结果打印出来。
这个例子仅使用了 INT32
类型,但是你可以根据实际需求使用其他 DBus 数据类型。你可以在接口定义中使用任意类型的组合来定义方法和信号。当你需要在方法和信号之间传递数据时,DBus 会自动处理类型转换和序列化。
接下来我将使用 C++ 和 sdbus-c++
库来展示如何实现上述计算器服务。首先,我们需要定义一个实现 com.example.Calculator
接口的类:
#include <sdbus-c++/sdbus-c++.h> class Calculator : public sdbus::AdaptorInterfaces<Calculator> { public: Calculator(sdbus::IConnection& connection, std::string objectPath) : AdaptorInterfaces(connection, std::move(objectPath)) { registerAdaptorInterface("com.example.Calculator"); } int32_t Add(int32_t a, int32_t b) { return a + b; } int32_t Multiply(int32_t a, int32_t b) { return a * b; } };
在这个类中,我们定义了 Add
和 Multiply
方法,它们分别接收两个 int32_t
类型的参数,返回 int32_t
类型的结果。
然后,我们需要编写一个主程序来启动这个服务:
#include <iostream> #include "Calculator.h" int main() { // 创建 DBus 系统总线连接 auto connection = sdbus::createSystemBusConnection(); // 创建 Calculator 对象并导出到 DBus Calculator calculator(*connection, "/com/example/Calculator"); // 运行消息循环 connection->enterEventLoop(); return 0; }
接下来,我们需要编写一个 C++ 客户端来调用这个服务。我们可以使用 sdbus-c++
库的代理类来实现:
#include <sdbus-c++/sdbus-c++.h> #include <iostream> class CalculatorProxy : public sdbus::ProxyInterfaces<CalculatorProxy> { public: CalculatorProxy(sdbus::IConnection& connection, std::string serviceName, std::string objectPath) : ProxyInterfaces(connection, std::move(serviceName), std::move(objectPath)) { registerProxyInterface("com.example.Calculator"); } int32_t Add(int32_t a, int32_t b) { return callMethod("Add").onInterface("com.example.Calculator").withArguments(a, b).storeResultsIn(a); } int32_t Multiply(int32_t a, int32_t b) { return callMethod("Multiply").onInterface("com.example.Calculator").withArguments(a, b).storeResultsIn(a); } }; int main() { // 创建 DBus 系统总线连接 auto connection = sdbus::createSystemBusConnection(); // 创建 CalculatorProxy 对象 CalculatorProxy calculator(*connection, "com.example.Calculator", "/com/example/Calculator"); int32_t a = 3, b = 4; // 调用 Add 方法 int32_t sum_result = calculator.Add(a, b); std::cout << "Sum: " << sum_result << std::endl; // 调用 Multiply 方法 int32_t product_result = calculator.Multiply(a, b); std::cout << "Product: " << product_result << std::endl; return 0; }
在客户端示例中,我们首先创建了 CalculatorProxy
类,用于调用 com.example.Calculator
服务的 Add
和 Multiply
方法。
复合类型(Compound Types)
当然可以。除了原始类型,DBus 还提供了一些复合类型,用于表示更复杂的数据结构。以下是对各种复合类型的文字描述:
- 数组(ARRAY):数组是一种包含多个相同类型元素的集合。在 DBus 中,数组用于表示一组相同类型的数据,例如整数列表、字符串列表等。数组中的元素类型可以是原始类型,也可以是其他复合类型。
- 结构(STRUCT):结构是一种包含多个不同类型元素的集合。在 DBus 中,结构用于表示一组具有固定格式和顺序的不同类型的数据。结构中的元素类型可以是原始类型,也可以是其他复合类型。
- 字典(DICT):字典是一种包含键值对的集合。在 DBus 中,字典用于表示一组具有唯一键和对应值的数据。字典的键类型必须是原始类型,而值类型可以是原始类型或其他复合类型。常见的字典类型是以字符串作为键的字典,如:
std::map
。 - 变体(VARIANT):变体是一种特殊的数据类型,可以包含任何其他类型的数据。在 DBus 中,变体用于表示动态类型的数据,即数据类型在运行时可以更改。变体可以包含原始类型和复合类型。使用变体时需要小心,因为它可能增加类型安全性的风险。
这些复合类型可以与原始类型结合使用,形成更复杂的数据结构。在 DBus 中,复合类型通常用于表示方法参数、返回值和信号等复杂数据。通过使用复合类型,可以在 DBus 通信协议中表示各种复杂的数据结构和层次关系。
复合类型 | 英文名称 | 描述 | 示例 | Qt 数据类型 | C++ 标准库数据类型 |
数组 | ARRAY | 包含多个相同类型元素的集合。用于表示一组相同类型的数据。元素类型可以是原始类型或其他复合类型。 | std::vector<int> 、std::vector<std::string> |
QList<T> 、QVector<T> |
std::vector<T> |
结构 | STRUCT | 包含多个不同类型元素的集合。用于表示一组具有固定格式和顺序的不同类型的数据。元素类型可以是原始类型或其他复合类型。 | std::tuple<int, double, std::string> |
QPair<T1, T2> 、QVariantList 或自定义 QVariant 结构 |
std::tuple<T1, T2, ...> 或自定义结构体 |
字典 | DICT | 包含键值对的集合。用于表示一组具有唯一键和对应值的数据。键类型必须是原始类型,值类型可以是原始类型或其他复合类型。 | std::map<std::string, int> |
QMap<Key, Value> 、QHash<Key, Value> |
std::map<Key, Value> 、std::unordered_map<Key, Value> |
变体 | VARIANT | 可以包含任何其他类型的数据。用于表示动态类型的数据,即数据类型在运行时可以更改。变体可以包含原始类型和复合类型。 | QVariant |
QVariant |
std::variant<T1, T2, ...> |
复合类型综合示例
结构体的示例
当然可以。下面是一个综合示例,展示了如何在 DBus 服务中使用这些原始类型和复合类型。我们将实现一个简单的信息服务,提供以下功能:
- 添加一名用户(包含 ID、姓名和年龄)
- 获取用户信息(根据 ID 获取用户信息)
- 获取用户年龄的平均值
首先,我们创建一个包含用户信息的结构:
struct UserInfo { uint32_t id; std::string name; uint16_t age; };
接下来,我们定义一个实现 com.example.InfoService
接口的类:
#include <sdbus-c++/sdbus-c++.h> #include <map> class InfoService : public sdbus::AdaptorInterfaces<InfoService> { public: InfoService(sdbus::IConnection& connection, std::string objectPath) : AdaptorInterfaces(connection, std::move(objectPath)) { registerAdaptorInterface("com.example.InfoService"); } void AddUser(uint32_t id, const std::string& name, uint16_t age) { UserInfo user {id, name, age}; users_[id] = user; } std::tuple<uint32_t, std::string, uint16_t> GetUser(uint32_t id) { const auto& user = users_.at(id); return std::make_tuple(user.id, user.name, user.age); } double GetAverageAge() { uint32_t total_age = 0; for (const auto& user : users_) { total_age += user.second.age; } return static_cast<double>(total_age) / users_.size(); } private: std::map<uint32_t, UserInfo> users_; };
对于 D-Bus 服务,接口定义可以用 XML 格式编写,这是 D-Bus 规范中推荐的方式。使用 XML 可以让接口定义与实现相互独立,便于跨语言和跨平台使用。
然而,在上面的示例中,我们使用了 C++ 语言和 sdbus-c++ 库来实现 D-Bus 服务,该库会自动生成 XML 格式的接口定义。在 sdbus-c++ 中,接口定义以 C++ 类的形式编写,库内部负责将其转换为 D-Bus 的 XML 表示。因此,我们不需要显式地编写 XML 文件,而是直接用 C++ 类定义接口。
但是,如果您希望在其他语言或 D-Bus 库中使用这个服务,您可能需要将接口定义转换为 XML 格式。在这种情况下,您可以使用 D-Feet 等工具在运行时检查 D-Bus 服务的接口定义,或者手动编写对应的 XML 文件。
以下是一个与上面示例相对应的 XML 接口定义:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="com.example.InfoService"> <method name="AddUser"> <arg type="u" name="id" direction="in"/> <arg type="s" name="name" direction="in"/> <arg type="q" name="age" direction="in"/> </method> <method name="GetUser"> <arg type="u" name="id" direction="in"/> <arg type="u" name="user_id" direction="out"/> <arg type="s" name="name" direction="out"/> <arg type="q" name="age" direction="out"/> </method> <method name="GetAverageAge"> <arg type="d" name="average_age" direction="out"/> </method> </interface> </node>
这个 XML 文件描述了 com.example.InfoService
接口及其方法和参数。如果需要,您可以将此 XML 文件用于其他 D-Bus 库或编程语言。
然后,我们需要编写一个主程序来启动这个服务:
#include <iostream> #include "InfoService.h" int main() { // 创建 DBus 系统总线连接 auto connection = sdbus::createSystemBusConnection(); // 创建 InfoService 对象并导出到 DBus InfoService info_service(*connection, "/com/example/InfoService"); // 运行消息循环 connection->enterEventLoop(); return 0; }
接下来,我们需要编写一个 C++ 客户端来调用这个服务。我们可以使用 sdbus-c++
库的代理类来实现:
#include <sdbus-c++/sdbus-c++.h> #include <iostream> class InfoServiceProxy : public sdbus::ProxyInterfaces<InfoServiceProxy> { public: InfoServiceProxy(sdbus::IConnection& connection, std::string serviceName, std::string objectPath) : ProxyInterfaces(connection, std::move(serviceName), std::move(objectPath)) { registerProxyInterface("com.example.InfoService"); } void AddUser(uint32_t id, const std::string& name, uint16_t age) { callMethod("AddUser").onInterface("com.example.InfoService").withArguments(id, name, age); } std::tuple<uint32_t, std::string, uint16_t> GetUser(uint32_t id) { uint32_t user_id; std::string name; uint16_t age; callMethod("GetUser").onInterface("com.example.InfoService").withArguments(id).storeResultsTo(user_id, name, age); return std::make_tuple(user_id, name, age); } double GetAverageAge() { double average_age; callMethod("GetAverageAge").onInterface("com.example.InfoService").storeResultsTo(average_age); return average_age; } }; int main() { // 创建 DBus 系统总线连接 auto connection = sdbus::createSystemBusConnection(); // 创建 InfoService 代理对象 InfoServiceProxy info_service_proxy(*connection, "com.example.InfoService", "/com/example/InfoService"); // 添加用户 info_service_proxy.AddUser(1, "Alice", 30); info_service_proxy.AddUser(2, "Bob", 35); // 获取用户信息 auto user_info = info_service_proxy.GetUser(1); std::cout << "User 1: ID = " << std::get<0>(user_info) << ", Name = " << std::get<1>(user_info) << ", Age = " << std::get<2>(user_info) << std::endl; // 获取平均年龄 double average_age = info_service_proxy.GetAverageAge(); std::cout << "Average age: " << average_age << std::endl; return 0;
这个综合示例展示了如何在一个简单的 DBus 服务中使用原始类型和复合类型。服务提供了添加用户、获取用户信息和计算平均年龄的方法,涉及到了整数、字符串、结构和字典等多种数据类型。客户端程序使用代理类调用服务的方法,并处理返回的数据。
下面是使用 Qt DBus 实现相同功能的示例。首先,我们需要在 .pro
文件中添加 QtDBus 模块:
首先,在 .pro
文件中添加 QtDBus 模块:
QT += core dbus
创建一个包含用户信息的结构:
// UserInfo.h #pragma once #include <QString> struct UserInfo { uint32_t id; QString name; uint16_t age; }; Q_DECLARE_METATYPE(UserInfo) // 声明自定义类型,使其可用于 Qt DBus
为了在 Qt DBus 中使用自定义数据类型,我们需要为其定义一个 QDBusArgument
的序列化和反序列化函数:
// UserInfo.cpp #include "UserInfo.h" #include <QDBusArgument> QDBusArgument& operator<<(QDBusArgument& argument, const UserInfo& user) { argument.beginStructure(); // 开始一个结构类型 argument << user.id << user.name << user.age; argument.endStructure(); // 结束一个结构类型 return argument; } const QDBusArgument& operator>>(const QDBusArgument& argument, UserInfo& user) { argument.beginStructure(); // 开始一个结构类型 argument >> user.id >> user.name >> user.age; argument.endStructure(); // 结束一个结构类型 return argument; }
接下来,创建一个实现 com.example.InfoService
接口的类:
// InfoService.h #pragma once #include <QDBusAbstractAdaptor> #include <QMap> #include "UserInfo.h" class InfoService : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.example.InfoService") public: InfoService(QObject* parent = nullptr) : QDBusAbstractAdaptor(parent) { } public slots: void AddUser(uint32_t id, const QString& name, uint16_t age) { UserInfo user {id, name, age}; users_[id] = user; } Q_SCRIPTABLE UserInfo GetUser(uint32_t id) { return users_[id]; } double GetAverageAge() { uint32_t total_age = 0; for (const auto& user : users_) { total_age += user.age; } return static_cast<double>(total_age) / users_.size(); } private: QMap<uint32_t, UserInfo> users_; };
编写一个主程序来启动这个服务:
// main_server.cpp #include <QCoreApplication> #include <QDBusConnection> #include "InfoService.h" #include "UserInfo.h" int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); qDBusRegisterMetaType<UserInfo>(); // 注册自定义类型 InfoService info_service; QDBusConnection::sessionBus().registerObject("/com/example/InfoService", &info_service, QDBusConnection::ExportAllSlots); return app.exec(); }
最后,编写一个 Qt 客户端来调用这个服务。我们可以使用 QDBusInterface
类来实现:
// main_client.cpp #include <QCoreApplication> #include <QDBusInterface> #include <QDBusReply> #include <QDebug> #include "UserInfo.h" int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); qDBusRegisterMetaType<UserInfo>(); // 注册自定义类型 // 创建一个 DBus 代理接口对象,用于访问远程服务 QDBusInterface info_service("com.example.InfoService", "/com/example/InfoService", "com.example.InfoService", QDBusConnection::sessionBus()); if (info_service.isValid()) // 检查接口是否有效 { // 调用服务的 AddUser 方法添加用户 info_service.call("AddUser", 1, "Alice", 30); info_service.call("AddUser", 2, "Bob", 35); // 调用服务的 GetUser 方法获取用户信息 QDBusReply<UserInfo> reply = info_service.call("GetUser", 1); if (reply.isValid()) // 检查返回值是否有效 { UserInfo user = reply.value(); qDebug() << "User 1: ID =" << user.id << ", Name =" << user.name << ", Age =" << user.age; } // 调用服务的 GetAverageAge 方法获取平均年龄 QDBusReply<double> average_age_reply = info_service.call("GetAverageAge"); if (average_age_reply.isValid()) // 检查返回值是否有效 { double average_age = average_age_reply.value(); qDebug() << "Average age:" << average_age; } } else { qDebug() << "Failed to connect to InfoService"; } return 0; }
这个客户端示例展示了如何使用 Qt DBus 连接到远程服务,调用方法,并处理返回的数据。我们首先创建了一个 QDBusInterface
对象来代表远程服务的接口。然后,我们检查接口是否有效,之后调用服务的方法,包括添加用户、获取用户信息和计算平均年龄。最后,我们处理方法的返回值,并在控制台上打印相关信息。
DBus类型系统以及在Qt和C++ 中的使用(二)https://developer.aliyun.com/article/1464206