zoukankan      html  css  js  c++  java
  • QtDBus快速入门

    QtDBus快速入门

    https://blog.51cto.com/9291927/2118184

    https://blog.51cto.com/9291927/2118468

    1、D-Bus简介

    D-Bus是Desktop Bus的缩写,是针对桌面环境优化的IPC(InterProcess Communication)机制,用于进程间的通信或进程与内核的通信。
    D-Bus是为Linux系统开发的进程间通信(IPC)和远程过程调用(RPC)机制,使用统一的通信协议来代替现有的各种IPC解决方案。D-Bus允许系统级进程(如:打印机和硬件驱动服务)和普通用户进程进行通信。
    D-Bus使用一个快速的二进制消息传递协议,D-Bus协议的低延迟和低消耗特点适用于同一台机器的通信。D-Bus的规范目前由freedesktop.org项目定义,可供所有团体使用。
    D-Bus不和低层的IPC直接竞争,比如sockets,shared memory或message queues。低层IPC有自己的特点,和D-Bus并不冲突。
    与其他重量级的进程间通信技术不同,D-Bus是非事务的。D-Bus使用了状态以及连接的概念,比UDP等底层消息传输协议更“聪明”。但另一方面,D-Bus传送的是离散消息,与TCP协议将数据看做“流”有所不同。D-Bus支持点对点的消息传递以及广播/订阅式的通信。

    2、不同IPC通信方式比较

    不同IPC通信机制的特点如下:
    A、CORBA是用于面向对象编程中复杂IPC的一个强大的解决方案。
    B、Bonobo是一个只用于GNOME的解决方案,基于CORBA并依赖于GObject。
    C、DCOP是一个较轻量级的IPC框架,功能较少,但可以很好地集成到KDE桌面环境中。
    D、SOAP和XML-RPC设计用于Web服务,因而使用HTTP作为其传输协议。
    E、D-BUS设计用于桌面应用程序和OS通信。

    3、D-Bus特性

    A、D-BUS的协议是低延迟而且低开销的,设计小巧且高效,以便最小化传送时间。从设计上避免往返交互并允许异步操作。
    B、协议是二进制的,而不是文本,排除序列化过程。
    C、考虑了字节序问题。
    D、易用性:按照消息而不是字节流来工作,并且自动地处理了许多困难的IPC问题,并且D-Bus库以可以封装的方式来设计,开发者可以使用框架里存在的对象/类型系统,而不用学习一种新的专用于IPC的对象/类型系统。
    E、请求时启动服务以及安全策略。
    F、支持多语言(C/C++/Java/C#/Python/Ruby),多平台(Linux/windows/maemo)。
    G、采用C语言,而不是C++。
    H、由于基本上不用于internet上的IPC,因此对本地IPC进行了特别优化。
    I、提供服务注册,理论上可以进行无限扩展。

    二、D-Bus架构

    1、D-Bus架构简介

    D-Bus是按一定的层次结构实现的,总体上D-Bus分为三层:
    A、接口层——接口层由libdbus库提供,进程通过libdbus库使用D-Bus的能力。通过底层库的接口可以实现两个进程之间进行连接并发送消息。
    B、总线层——由消息总线守护进程(message bus daemon )提供,消息总线守护进程是基于libdbus底层库的,可以路由消息。消息总线守护进程负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传递。
    C、封装层——封装层是一系列基于特定应用程序框架的Wrapper库,将D-Bus底层接口封装成方便用户使用的通用API。

    2、D-Bus接口层

    libdbus只支持点对点的通信,即只支持一进程与另外的一个进程进行通信。通信是基于消息的,消息包含头部和消息体。
    libdbus提供C语言的底层API,API是为了将D-Bus绑定到特定的对象或是语言而设计的,官方文档中建议不要在应用上直接使用D-Bus的底层接口,推荐使用D-Bus的绑定,如QtDBus、GDBus、dbus-c++等实现。

    3、D-Bus总线层

    D-Bus总线层由消息总线守护进程(message bus daemon )提供。消息总线守护进程是一个后台进程,是/usr/bin/dbus-daemon的一个运行实例,  负责消息的转发,dbus-daemon运行时会调用libdus的库。应用程序调用特定的应用程序框架的Wrapper库与dbus-daemon进行通信。应用程序通过D-Bus与其它进程通信必须先建立到消息总线守护进程实例的连接。
    最常见的基于dbus的程序也符合C/S结构。比如开发两个程序A和B,其中A是客户端,B是服务端。假设A要调用B的一个函数func,那么实际的消息流动方向是:A告诉dbus-daemon请求要调用B的func函数,然后dbus-daemon去调用B的func函数,如果func有返回值的话,B会把返回值告诉dbus-daemon,然后dbus- daemon再把返回值告诉A。如果B进程还没有启动,则dbus-daemon会自动的先把B进程启动起来。
    通常情况下,Linux会有两个dbus-daemon进程,一个属于system,一个属于session,在用户登录的时候由dbus-launch启动。
    大多数普通程序,都是使用session的dbus-daemon,默认情况下,A就是将消息发给属于session的dbus-daemon。
    dbus-daemon是有地址的,环境变量DBUS_SESSION_BUS_ADDRESS用于表示当前登录用户的session的dbus-daemon进程的地址,可以使用echo $DBUS_SESSION_BUS_ADDRESS查看。

    当用户登录进桌面环境的时候,系统启动脚本会调用到dbus-launch来启动一个dbus-daemon进程,同时会把启动的dbus-daemon地址赋予环境变量DBUS_SESSION_BUS_ADDRESS。
    一般情况下,不需要考虑DBUS_SESSION_BUS_ADDRESS,但某些时候,单独启动一个dbus-daemon有助于程序的调试。
    利用dbus-daemon自启动机制运行的服务进程,都是后台进程,标准输出设备已经被重定向,如果B进程有一些调试用的打印信息输出,则很难直接查看。此时,可以单独启动一个dbus-daemon,让A和B都使用自己启动的dbus-daemon,此时,dbus-daemon能把B的打印信息显示出来。
    先在终端下启动一个dbus-daemon,命令如下形式如下:
         DBUS_VERBOSE=1 dbus-daemon --session --print-address
    如此启动的dbus-daemon会前台执行,并且打印出地址。

    然后,在执行A程序的时候,设置环境变量DBUS_SESSION_BUS_ADDRESS为刚才得到的地址值。
    DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-7MlJMxxGnW,guid=437c0e6060516670cfccacc15afc43c6 ./A
    此时运行程序A和B,使用自己启动的dbus-daemon来转发消息,并且会把B的打印信息显示出来。
    消息总线守护进程是一个特殊的进程,用于管理系统内的总线,可以将一个进程的消息路由给另外一个进程。如果有很多应用程序连接到消息总线守护进程的总线上,总线能把消息路由到对应的一个或多个进程中去。因此在总线层上,实现了点对点通信的支持,也实现了广播/订阅通信方式。
    在最底层,D-Bus只支持点对点的通信,一般使用本地套接字(AF_UNIX)在应用和消息总线守护进程之间通信。D-Bus的点对点是经过bus daemon抽象过的,由bus daemon来完成寻址和发送消息,因此每个应用不必关心要把消息发给哪个进程。D-Bus发送消息通常包含如下步骤:
    A、应用程序创建和发送消息给消息总线守护进程。
    B、消息总线守护进程对收到的消息进行分发处理。
    C、目标程序接收到消息,然后根据消息的种类,做不同的响应:确认、应答、忽略。
    总线是D-Bus的进程间通信机制,一个系统中通常存在多条总线,总线由D-Bus总线守护进程管理。
    最重要的总线为系统总线(System Bus),Linux内核引导时,系统总线就已被装入内存。只有Linux内核、Linux桌面环境和权限较高的程序才能向系统总线写入消息,以此保障系统安全性,防止有恶意进程假冒Linux发送消息。
    会话总线(Session Buses)由普通进程创建,可同时存在多条。会话总线属于某个进程私有,用于进程间传递消息。

    4、D-Bus封装层

    D-Bus封装层是将libdbus底层API绑定到特定的对象系统或是语言中,將不便使用的libdbus底层API封裝成可以在应用层使用的高級API,如libdbus-glib、libdbus-qt等。
    D-Bus在很多不同的编程语言上都有其接口实现。不同语言的接口封装了D-Bus低级API,提供了更符合编程语言的语法结构。
    实现D-Bus接口的语言正在逐渐增加。在C语言中,有最底层的API,但其实现及使用上非常复杂。C语言中另一个实用化的实现基于GLib。在Java、Perl、Python等都有D-Bus接口实现。

    三、D-Bus术语

    1、D-Bus术语简介

    总线是消息总线守护进程(message bus daemon)的运行实例,每个总线都有一个地址,应用进程就是通过总线地址和相应的总线连接的。总线上的每一个连接都有一个连接名,连接名也称bus name。每个连接上有至少一个对象,通常有多个对象,对象使用对象路径唯一标识。对象要实现一个或多个接口,每个接口包含一组方法和信号。

    2、总线(Bus)

    在D-Bus中,总线(bus)是核心的概念:不同的程序可以通过总线进行某些操作,比如方法调用、发送信号和监听特定的信号。总线通常有两种,系统总线(system bus)和用户总线(session bus),系统总线通常只有一条,用户总线在用户登录时创建。
    系统总线是一个持久的总线,在系统启动时就创建,供系统内核和后台进程使用,具有较高的安全性。系统总线最常用是发送系统消息,比如:插入一个新的存储设备、有新的网络连接等。
    会话总线是在某个用户登录后启动,属于某个用户私有,是某用户的应用程序用来通话的通道。在很多嵌入式系统中,只有一个用户ID登录运行,因此只有一个会话总线。
    一条消息总线就是一个消息路由器,是消息总线守护进程(message bus daemon)的一个实例。

    3、地址(Address)

    使用d-bus的应用程序既可以是server端也可以是client端,server端监听到来的连接,client端连接到server端,一旦连接建立,消息就可以流转。如果使用dbus daemon,所有的应用程序都是client端,dbus daemon监听所有的连接,应用程序初始化连接到dbus daemon。
    每一条总线都有一个地址,进程通过总线的地址连接到总线上。一个D-Bus的地址是指server端用于监听,client端将要连接的地方,例如unix:path=/tmp/abcedf标识server端将在路径/tmp/abcedf的UNIX domain socket监听,client端将要连接到这个地址。地址可以是指定的TCP/IP socket或者其他在或者将在D-Bus协议中定义的传输方式。
    如果使用bus daemon,libdbus将通过读取环境变量DBUS_SESSION_BUS_ADDRESS自动获取session bus damon的地址,通过检查一个指定的UNIX domain socket路径获取system bus的地址。
    如果使用D-bus,但不是daemon,需要定义哪个应用是server端,哪个是client端,并定义一套机制用于认可server端的地址。

    4、连接名(Bus Name)

    总线上的每个连接都有一个或多个名字。当连接建立以后,D-Bus 服务会分配一个不可改变的连接名,称为唯一连接名(unique connection name),唯一连接名即使在进程结束后也不会再被其他进程所使用。唯一连接名以冒号开头,如“:34-907”。但唯一连接名总是临时分配,无法确定,也难以记忆,因此应用可以要求有另外一个名字公共名(well-known name)来对应唯一连接名。例如可以使用“com.mycompany”来映射“:34-907”。应用程序可能会要求拥有额外的公共名(well-known name)。例如,可以写一个规范来定义一个名字叫做 com.mycompany.TextEditor。协议可以指定自己拥有名字为com.mycompany.TextEditor的连接,一个路径为/com/mycompany/TextFileManager的对象,对象拥有接口org.freedesktop.FileHandler。应用程序就可以发送消息到总线上的连接名字,对象和接口以执行方法调用。
    连接名可以用于跟踪应用程序的生命周期。当应用退出(或者崩溃)时,与总线的连接将被OS内核关掉,总线将会发送通知,告诉剩余的应用程序。

    5、对象和对象路径(Object and Object Path)

    D-Bus的对象和面向对象语言中的对象含义是不同的,D-Bus的对象表示的是D-Bus通道中信息流向的端点。对象由客户进程创建,并在连接进程中保持不变。
    所有使用D-BUS的应用程序都包含一些对象, 当经由一个D-BUS连接收到一条消息时,消息是被发往一个对象而不是整个应用程序。应用程序框架中定义了这样的对象,如GObject,QObject等,在D-Bus中称为原生对象(native object)。
    对于底层的D-Bus协议,即libdbus API,并不理会原生对象,使用对象路径(object path)的概念。通过对象路径,高层API接口可以绑定到对象,允许远程应用指向对象。对象路径如同文件系统路径,例如一个对象可能叫做“/org/kde/kspread/sheets/3/cells/4/5”。
    对象路径在全局(session或者system)是唯一的,用于消息的路由。

    6、接口(Interface)

    每一个对象支持一个或者多个接口,接口是一组方法和信号的集和,接口定义一个对象实体的类型。D-Bus对接口的命名方式,类似org.freedesktop.Introspectable。开发人员通常使用编程语言名字作为接口名字。

    7、方法(Methods)

    每一个对象有两类成员:方法和信号。方法是一段函数代码,带有输入和输出;信号是广播给所有兴趣的其他实体,信号可以带有数据payload。
    客户向某对象发送一个请求,即对象被请求执行一个明确的、有名称的动作。如果客户请求执行一个目标对象未提供的方法,将会产生一个错误。方法的定义可以支持输入参数。对于每个请求,都有一个包含请求结果以及结果数据(输出参数)的响应返回给请求者。当请求无法完成时,响应中将包含异常信息,其中至少有异常名称以及错误信息。
    大多数语言都将这些封装在自身的语言机制中,比如将参数包装进消息包,将异常信息转换成语言自身的异常等等。在这些实现中,向远程对象传递一个字符串参数就好像是在本地执行一个字符串参数的函数一样简单。此时不再需要数据类型转换、数据复制等繁琐工作,语言本身封装了一切底层实现。

    8、信号(Signals)

    信号依然遵从面向对象概念,信号是从对象发出但没有特定目的地址的单向数据广播。客户进程可以预先注册其感兴趣的信号,如特定名称的信号或从某个对象发出的信号等。当对象发出信号后,所有订阅了该信号的客户进程将收到此信号的复本。接收端可能有多种情况出现,或者有一个客户进程,或者有多个客户进程,或者根本没有客户进程对这个信号感兴趣。对于信号来说没有响应消息,发出信号的对象不会知道是不是有客户进程在接收,有多少客户进程接收,以及从客户进程收到任何反馈。
    信号可以有参数。但信号是单向通信,因此不可能像方法一样具有输入输出参数。D-Bus允许客户进程通过参数比对过滤其需要的信号。
    信号一般用来广播一些客户可能会感兴趣的事件,比如某个其它的客户进程与总线的连接断开等。这些信号来自总线对象,因此从信号中客户进程可以分辨断线是由于正常退出、被杀掉或者程序崩溃。

    9、代理(Proxies)

    代理对象用来表示其他的remote object。当触发了proxy对象的method时,将会在D-Bus上发送一个method_call的消息,并等待答复,根据答复返回。
    总线上的对象一般通过代理来访问。总线上的对象位于客户进程以外,而客户可以调用本地接口与对象通信,此时,本地接口充当了代理的角色。当触发了代理对象的方法时,将会在D-Bus上发送一个method_call的消息,并等待答复返回,就象使用一个本地对象一样。
    一些语言的代理支持“断线重连”。比如所连接的对象在某段时间里暂时断开了与总线的连接,代理会自动重连到相同的连接名并重新找到对象,程序甚至不会知道目标对象有段时间不可用。并不是所有的语言都支持这一特性,在GLib中的两种代理中的一种支持。
    比如不用代理时的代码如下:

    Message message = new Message("/remote/object/path", "MethodName", arg1, arg2); 
    Connection connection = getBusConnection();           
    connection.send(message);           
    Message reply = connection.waitForReply(message);           
    if (reply.isError()) {                         
    }
     else {              
    Object returnValue = reply.getReturnValue();           
    } 

    采用代理时对应的代码则是:

    Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");           
    Object returnValue = proxy.MethodName(arg1, arg2);

    10、服务

    服务是 D-BUS 的最高层次抽象,服务的实现当前还在不断发展变化。应用程序可以通过一个总线来注册一个服务,如果成功,则应用程序就已经获得了服务。其他应用程序可以检查在总线上是否已经存在一个特定的服务,如果没有可以要求总线启动它。
    服务是 D-BUS 的最高层次抽象,服务的实现当前还在不断发展变化。应用程序可以通过一个总线来注册一个服务,如果成功,则应用程序就已经获得了服务。其他应用程序可以检查在总线上是否已经存在一个特定的服务,如果没有可以要求总线启动它。
    当通过总线进行通信时,应用程序会获取服务名称。服务名称是应用程序如何选择同一总线上的其他应用程序的依据。服务名称由总线守护进程进行代理,用于将消息从一个应用程序路由到另一个应用程序。服务名称的类似概念是IP地址和主机名:计算机通常具有一个IP地址,并且根据其向网络提供的服务,可以具有与之相关联的一个或多个主机名。
    另一方面,如果不使用总线,服务名称也不会使用。如果再将它与计算机网络进行比较,这将等同于点到点网络:因为对等方是已知的,所以不需要使用主机名来查找它或它的IP地址。
    总线服务名称的格式实际上与主机名非常相似:它是字母和数字的点分隔序列。常见的做法是根据服务定义的组织的域名来命名自己的服务名称。例如,D-Bus服务由freedesktop.org定义,可以使用org.freedesktop.DBus服务名称在总线上找到它。

    四、消息和消息总线

    1、消息简介

    D-Bus通信机制是通过进程间发送消息实现的,最基本的D-Bus协议是一对一的通信协议。与socket通信不同,D-Bus是面向消息的协议。但如果使用高层的绑定接口,不会直接接触到D-Bus的消息。
    D-Bus 有四种类型的消息:
    A、method_call方法调用
    B、method_return方法返回
    C、error错误
    D、signal信号
    代理中的远程对象调用涉及到了消息总线以及method_call和method_return两类消息。
    消息有消息头(header)和消息体(body)。消息头包含消息体的路由信息,消息体是净荷,通常包含的是参数。消息头通常包含发送进程的连接名(Bus Name)、方法或者信号名等等,其中有一字段是用于描述消息体中的参数的类型的,例如“i”标识32位整数,“ii”表示2个32位整数。

    2、调用method的流程

    进程A要调用进程B的一个method,进程A发送method_call消息到进程B,进程B回复method_return消息。在发送消息时,发送方会在消息中添加不同的序列号,同样,回复消息中也会含有序列号,以便对应。
    调用method的流程如下:
    A、在发送method_call消息时,如果使用了代理,进程A要调用进程B的某方法,不用构造method_call消息,只需调用代理的本地方法,代理会自动生成method_call消息发送到消息总线上。
    B、如果使用底层API,进程A需要构造一个method_call消息。method_call消息包含对应进程B的连接名、方法名、方法所需参数、进程B中的对象路径和进程B中声明此方法的接口。
    C、将method_call消息发送到消息总线上。
    D、信息总线检查消息头中的目的连接名,当找到一个进程与此连接名对应时发送消息到该进程。当找不到一个进程与此连接名对应时,返回给进程A一个error消息。
    E、进程B解析消息,如果是采用底层API方式,则直接调用方法,然后发宋应答消息到消息总线。如果是D-BUs高级API接口,会先检测对象路径、接口、方法名称,然后把消息转换成对应的本地对象(如GObject,QT中的QObject等)的方法,调用本地对象方法后再将应答结果转换成应答消息发给消息总线。
    F、消息总线接收到method_return消息,将把method_return消息直接发给发出调用消息的进程。
    消息总线不会对总线上的消息进行重排序。如果发送了两条消息到同一个进程,将按照发送顺序接收到。接收进程不需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。

    3、发送signal的流程

    发送信号是单向广播的,信号的发送者不知道对信号作响应的有哪些进程,所以信号发送是没有返回值的。信号接收者通过向消息总线注册匹配规则来表示对某信号感兴趣,而匹配规则通常包含信号的发出者和信号名。
    信号发送的流程如下:
    A、当使用dbus底层接口时,信号需要应用进程自己创建和发送到消息总线;使用dbus高层API接口时,可以使用相关对象进行发送。信号消息包含有声明信号的接口名、信号名、所在进程对应的连接名和相关参数。
    B、连接到消息总线上的进程如果对某个信号感兴趣,则注册相应的匹配规则。消息总线保持有匹配规则表。
    C、消息总线根据匹配规则表,将信号发送到对信号感兴趣的进程。
    D、每个进程收到信号后,如果使用dbus高级API接口,可以选择触发代理对象上的信号。如果使用dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。

    4、DBus工具

    D-Bus提供了两个小工具:dbus-send 和dbus-monitor。可以用dbus-send发送消息,用dbus-monitor监视通道上流动的消息。
    dbus-send
    用于发送一个消息到消息通道上,使用格式如下:

    dbus-send [--system | --session] --type=TYPE --print-reply --dest=连接名对象路径接口名.方法名参数类型:参数值参数类型:参数值
    dbus-send --session --type=method_call --print-reply --dest=连接名对象路径接口名.方法名 参数类型:参数值 参数类型:参数值

    dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolean。
    dbus-monitor
    用于打印消息通道上的消息,使用格式如下:

    dbus-monitor [--system | --session | --address ADDRESS] [--profile | --monitor] [watch expressions]
    dbus-monitor "type='signal', sender='org.gnome.TypingMonitor', interface='org.gnome.TypingMonitor'"

    5、消息总线上的方法和信号

    消息总线是一个特殊的应用,主要关于消息总线上的方法和信号。
    A、Introspection
    消息总线上有一个接口org.freedesktop.DBus.Introspectable,接口中声明了一个方法Introspect,不带参数,将返回一个XML string,XML字符串描述接口、方法、信号。
    B、消息总线上的方法和信号
    可以通过向名称为“org.freedesktop.DBus”的连接上的对象“/”发送消息来调用消息总线提供的方法。消息总线对象支持标准接口"org.freedesktop.DBus.Introspectable",可以调用org.freedesktop.DBus.Introspectable.Introspect方法查看消息总线对象支持的接口。
    dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect
    用户总线对象支持标准接口“org.freedesktop.DBus.Introspectable”和接口 “org.freedesktop.DBus”。接口“org.freedesktop.DBus”有18个方法和3个信号。

    <interface name="org.freedesktop.DBus">
        <method name="Hello">
          <arg direction="out" type="s"/>
        </method>
        <method name="RequestName">
          <arg direction="in" type="s"/>
          <arg direction="in" type="u"/>
          <arg direction="out" type="u"/>
        </method>
        <method name="ReleaseName">
          <arg direction="in" type="s"/>
          <arg direction="out" type="u"/>
        </method>
        <method name="StartServiceByName">
          <arg direction="in" type="s"/>
          <arg direction="in" type="u"/>
          <arg direction="out" type="u"/>
        </method>
        <method name="UpdateActivationEnvironment">
          <arg direction="in" type="a{ss}"/>
        </method>
        <method name="NameHasOwner">
          <arg direction="in" type="s"/>
          <arg direction="out" type="b"/>
        </method>
        <method name="ListNames">
          <arg direction="out" type="as"/>
        </method>
        <method name="ListActivatableNames">
          <arg direction="out" type="as"/>
        </method>
        <method name="AddMatch">
          <arg direction="in" type="s"/>
        </method>
        <method name="RemoveMatch">
          <arg direction="in" type="s"/>
        </method>
        <method name="GetNameOwner">
          <arg direction="in" type="s"/>
          <arg direction="out" type="s"/>
        </method>
        <method name="ListQueuedOwners">
          <arg direction="in" type="s"/>
          <arg direction="out" type="as"/>
        </method>
        <method name="GetConnectionUnixUser">
          <arg direction="in" type="s"/>
          <arg direction="out" type="u"/>
        </method>
        <method name="GetConnectionUnixProcessID">
          <arg direction="in" type="s"/>
          <arg direction="out" type="u"/>
        </method>
        <method name="GetAdtAuditSessionData">
          <arg direction="in" type="s"/>
          <arg direction="out" type="ay"/>
        </method>
        <method name="GetConnectionSELinuxSecurityContext">
          <arg direction="in" type="s"/>
          <arg direction="out" type="ay"/>
        </method>
        <method name="ReloadConfig">
        </method>
        <method name="GetId">
          <arg direction="out" type="s"/>
        </method>
        <signal name="NameOwnerChanged">
          <arg type="s"/>
          <arg type="s"/>
          <arg type="s"/>
        </signal>
        <signal name="NameLost">
          <arg type="s"/>
        </signal>
        <signal name="NameAcquired">
          <arg type="s"/>
        </signal>
      </interface>
      <interface name="org.freedesktop.DBus.Introspectable">
        <method name="Introspect">
          <arg direction="out" type="s"/>
        </method>
      </interface>

    一、QtDBus简介

    QtDBus是一个使用D-Bus协议进行进程间通信的仅在Unix运行的库,是对D-Bus底层API的封装实现。
    QtDBus模块提供了使用Qt信号槽机制扩展的接口。要使用QtDBus模块,需要在代码中加入以下代码:

    #include <QtDBus>

    如果使用qmake构建程序,需要在工程文件中增加下列代码来链接QtDBus库:

    QT += qdbus

    二、QtDBus类型系统

    1、QtDBus类型系统简介

    D-Bus有一种基于几种原生与在数组和结构中的原生类型组成的复合类型的扩展类型系统。QtDBus模块通过QDBusArgument类实现了类型系统,允许用户通过总线发送和接收每一种C++类型。

    2、原生类型

    QtDBus通过QDBusArgument支持原生类型,不需要特殊的定制。

    Qt类型                                     D-Bus类型
    uchar                                        BYTE
    bool                                          BOOLEAN
    short                                         INT16
    ushort                                       UINT16
    int                                             INT32
    uint                                           UINT32
    qlonglong                                 INT64
    qulonglong                               UINT64
    double                                      DOUBLE
    QString                                    STRING
    QDBusVariant                          VARIANT
    QDBusObjectPath                   OBJECT_PATH
    QDBusSignature                      SIGNATURE

    除了原生类型,QDBusArgument也支持在Qt应用中广泛使用的两种非原生类型,QStringList和QByteArray。

    3、复合类型

    D-Bus指定由原生类型聚合而成的三种复合类型:ARRAY、STRUCT和 maps/dictionaries。ARRAY零个或多个相同元素的集合,STRUCT是由不同类型的固定数量的元素组成的集合,Maps or dictionaries是元素对的数组,一个map中可以有零个或多个元素。

    4、扩展类型系统

    为了在QtDBus模块使用自定义类型,自定义类性必须使用Q_DECLARE_METATYPE()声明为Qt元类型,使用qDBusRegisterMetaType()函数注册。流操作符会被注册系统自动找到。
    QtDBus模块为Qt容器类使用数组和map提供了模板特化,例如QMap和QList,不必实现流操作符函数。对于其它的类型,流操作符必须显示实现。

    5、类型系统使用

    QtDBus定义的所有类型能用于通过总线发送和接收消息。不能使用上述类型之外的任何类型,包括typedefs定义的列表类型,如

    QList<QVariant>`和`QMap< QString,QVariant>

    三、QtDBus常用类

    1、QDBusMessage

    QDBusMessage类表示D-Bus总线发送或接收的一个消息。
    QDBusMessage对象代表总线上四种消息类型中的一种,四种消息类型如下:
    A、Method calls
    B、Method return values
    C、Signal emissions
    D、Error codes
    可以使用静态函数createError()、createMethodCall()、createSignal()创建消息。使用QDBusConnection::send() 函数发送消息。

    2、QDBusConnection

    QDBusConnection代表到D-Bus总线的一个连接,是一个D-Bus会话的起始点。通过QDBusConnection连接对象,可以访问远程对象、接口,连接远程信号到本地槽函数,注册对象等。
    D-Bus连接通过connectToBus()函数创建,connectToBus()函数会创建一个到总线服务端的连接,完成初始化工作,并关联一个连接名到连接。
    使用disconnectFromBus()函数会断开连接。一旦断开连接后,调用connectToBus()函数将不会重建连接,必须创建新的QDBusConnection实例。
    作为两种最常用总线类型的辅助,sessionBus()和systemBus()函数分别创建到会话在总线和系统总线的连接并返回,会在初次使用时打开,在QCoreApplication析构函数调用时断开。
    D-Bus支持点对点通信,不必使用总线服务。两个应用程序可以直接交流和交换消息。可以通过传递一个地址到connectToBus()函数实现。
    QDBusConnection connectToBus(BusType type, const QString & name)
    打开一个type类型的连接,并关联name连接名,返回关联本连接的QDBusConnection对象。
    QDBusConnection connectToBus(const QString & address, const QString & name)
    打开一个地址为address的私有总线,并关联name连接名,返回关联本连接的QDBusConnection对象。
    QDBusConnection connectToPeer(const QString & address, const QString & name)
    打开一个点对点的连接到address地址,并关联name连接名,返回关联本连接的QDBusConnection对象。
    void disconnectFromBus(const QString & name)
    关闭名为name的总线连接
    void disconnectFromPeer(const QString & name)
    关闭名为name的对等连接
    QByteArray localMachineId()
    返回一个D-Bus总线系统知道的本机ID
    QDBusConnection sender()
    返回发送信号的连接
    QDBusConnection sessionBus()
    返回一个打开到session总线的QDBusConnection对象
    QDBusConnection systemBus()
    返回一个打开到system总线的QDBusConnection对象
    QDBusPendingCall asyncCall(const QDBusMessage & message, int timeout = -1)const
    发送message消息到连接,并立即返回。本函数只支持method调用。返回一个用于追踪应答的QDBusPendingCall对象。
    QDBusMessage call(const QDBusMessage & message, QDBus::CallMode mode = QDBus::Block, int timeout = -1 ) const
    通过本连接发送消息message,并且阻塞,等待应答。
    bool registerObject(const QString & path, QObject * object, RegisterOptions options = ExportAdaptors)
    注册object对象到路径path,options选项指定由多少对象会被暴露到D-Bus总线,如果注册成功,返回true。
    bool registerService(const QString & serviceName)
    试图在D-Bus总线上注册serviceName服务,如果注册成功,返回true;如果名字已经在其它应用被注册,则注册失败。

    3、QDBusInterface

    QDBusInterface是远程对象接口的代理。
    QDBusInterface是一种通用的访问器类,用于调用远程对象,连接到远程对象导出的信号,获取/设置远程属性的值。当没有生成表示远程接口的生成代码时时,QDBusInterface类对远程对象的动态访问非常有用。
    调用通常是通过使用call()函数来实现,call函数构造消息,通过总线发送消息,等待应答并解码应答。信号使用QObject::connect()函数进行连接。最终,使用QObject::property()和QObject::setProperty()函数对属性进行访问。

    4、QDBusReply

    QDBusReply类用于存储对远程对象的方法调用的应答。
    一个QDBusReply对象是方法调用的应答QDBusMessage对象的一个子集。QDBusReply对象只包含第一个输出参数或错误代码,并由QDBusInterface派生类使用,以允许将错误代码返回为函数的返回参数。

    QDBusReply<QString> reply = interface->call("RemoteMethod");
     if (reply.isValid())
         // use the returned value
         useValue(reply.value());
     else
         // call failed. Show an error condition.
         showError(reply.error());

    对于没有输出参数或返回值的远程调用,使用isValid()函数测试应答是否成功。

    5、QDBusAbstractAdaptor

    QDBusAbstractAdaptor类使用D-Bus Adaptor基类。
    QDBusAbstractAdaptor类是用于使用D-Bus向外部提供接口的所有对象的起点。可以通过将一个或多个派生自QDBusAbstractAdaptor的类附加到一个普通QObject对象上,使用QDBusConnection::registerObject注册QObject对象可以实现。QDBusAbstractAdaptor是一个轻量级封装,主要用于中继调用实际对象及其信号。
    每个QDBusAbstractAdaptor派生类都应该使用类定义中的Q_CLASSINFO宏来定义D-Bus接口。注意,这种方式只有一个接口可以暴露。
    QDBusAbstractAdaptor使用了信号、槽、属性的标准QObject机制来决定哪些信号、槽、属性被暴露到总线。任何QDBusAbstractAdaptor派生类发送的信号通过任何D-Bus连接自动中继到注册的对象上。
    QDBusAbstractAdaptor派生类对象必须使用new创建在堆上,不必由用户删除。

    6、QDBusAbstractInterface

    QDBusAbstractInterface是QtDBus模块中允许访问远程接口的所有D-Bus接口的基类。
    自动生成的代码类也继承自QDBusAbstractInterface,此描述的所有方法在生成的代码中也有效。除了此处的描述,生成代码类为远程方法提供了成员函数,允许在编译时检查正确参数和返回值,以及匹配的属性类型和匹配的信号参数。

    QDBusPendingCall asyncCall(const QString & method, 
                               const QVariant & arg1 = QVariant(), 
                               const QVariant & arg2 = QVariant(), 
                               const QVariant & arg3 = QVariant(), 
                               const QVariant & arg4 = QVariant(),
                               const QVariant & arg5 = QVariant(), 
                               const QVariant & arg6 = QVariant(), 
                               const QVariant & arg7 = QVariant(), 
                               const QVariant & arg8 = QVariant())

    调用本接口中的method方法,传递参数到远程的method。
    要调用的参数会通过D-Bus输入参数传递到远程方法,返回的QDBusPendingCall对象用于定义应答信息。
    本函数最多有8个参数,如果参数多于8个,或是传递可变数量的参数,使用asyncCallWithArgumentList()函数。

    QString value = retrieveValue();
    QDBusPendingCall pcall = interface->asyncCall(QLatin1String("Process"), value);
    
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
    
    QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
                     this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));

    7、QDBusArgument

    QDBusArgument类用于整理和分发D-Bus参数。QDBusArgument用于通过D-Bus发送参数到远程应用,并接收返回。
    QDBusArgument是QtDBus类型系统的核心类,QtDBus类型系统用于解析和原生类型。复合类型可以通过在数组、词典或结构中使用一个或多个原生类型创建。
    下列代码展示了使用QtDBus类型系统构造的包含一个整数和字符串的结构。

    struct MyStructure
     {
         int count;
         QString name;
     };
     Q_DECLARE_METATYPE(MyStructure)
    // Marshall the MyStructure data into a D-Bus argument
    QDBusArgument &operator<<(QDBusArgument &argument, const MyStructure &mystruct)
    {
        argument.beginStructure();
        argument << mystruct.count << mystruct.name;
        argument.endStructure();
        return argument;
    }
    
    // Retrieve the MyStructure data from the D-Bus argument
    const QDBusArgument &operator>>(const QDBusArgument &argument, MyStructure &mystruct)
    {
        argument.beginStructure();
        argument >> mystruct.count >> mystruct.name;
        argument.endStructure();
        return argument;
    }

    在QDBusArgument使用这个结构前,必须使用qDBusRegisterMetaType()函数进行注册。因此,在程序中应该则增加如下代码:
    qDBusRegisterMetaType&lt;MyStructure&gt;();
    一旦注册,类型可以在呼出方法调用(QDBusAbstractInterface::call())、来自注册对象的信号发射或来自远程应用的传入调用。

    8、QDBusConnectionInterface

    QDBusConnectionInterface类提供了对D-Bus总线服务的访问。
    D-Bus总线服务端中提供了一个特殊的接口org.freedesktop.DBus,允许客户端运行访问总线的某些属性,例如当前连接的客户端列表,QDBusConnectionInterface类提供对org.freedesktop.DBus接口的访问。
    本类中最常用的是使用registerService()和unregisterService()在总线上注册和注销服务名。
    QDBusConnectionInterface类定义四个信号,在总线上有服务状态变化时发送。

    void callWithCallbackFailed(const QDBusError & error, const QDBusMessage & call)
    void serviceOwnerChanged(const QString & name, const QString & oldOwner, const QString & newOwner)
    void serviceRegistered(const QString & serviceName)
    void serviceUnregistered(const QString & serviceName)

    9、QDBusVariant

    QDBusVariant类使程序员能够识别由D-Bus类型系统提供的Variant类型。一个使用整数、D-Bus变体类型和字符串作为参数的D-Bus函数可以使用如下的参数列表调用。

    QList<QVariant> arguments;
    arguments << QVariant(42) << QVariant::fromValue(QDBusVariant(43)) << QVariant("hello");
    myDBusMessage.setArguments(arguments);

    当D-Bus函数返回一个D-Bus变体类型时,可以使用如下方法获取:

    // call a D-Bus function that returns a D-Bus variant
    QVariant v = callMyDBusFunction();
    // retrieve the D-Bus variant
    QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(v);
    // retrieve the actual value stored in the D-Bus variant
    QVariant result = dbusVariant.variant();

    QDBusVariant中的QVariant需要区分一个正常的D-Bus值和一个QDBusVariant中的值。

    四、QtDBus工具

    1、qdbusviewer

    qdbusviewer用于查看D-Bus总线上的服务、对象、接口以及接口的method。使用方法直接在命令行执行:qdbusviewer

    2、qdbuscpp2xml

    qdbuscpp2xml会解析QObject派生类的C++头文件或是源文件,生成D-Bus的内省xml文件。qdbuscpp2xml 会区分函数的输入输出,如果参数声明为const则会是输入,否则可能会被当作输出。
    qdbuscpp2xml使用语法如下:
    qdbuscpp2xml [options...] [files...]
    Options参数如下:
    -p|-s|-m:只解析脚本化的属性、信号、方法(槽函数)
    -P|-S|-M:解析所有的属性、信号、方法(槽函数)
    -a:输出所有的脚本化内容,等价于-psm
    -A:输出所有的内容,等价于-PSM
    -o filename:输出内容到filename文件
    解析所有的方法输出到com.scorpio.test.xml文件命令如下:
    qdbuscpp2xml -M test.h -o com.scorpio.test.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.scorpio.test.value">
        <method name="maxValue">
          <arg type="i" direction="out"/>
        </method>
        <method name="minValue">
          <arg type="i" direction="out"/>
        </method>
        <method name="value">
          <arg type="i" direction="out"/>
        </method>
        <method name="setValue">
          <arg name="value" type="i" direction="in"/>
        </method>
      </interface>
    </node>

    3、qdbusxml2cpp

    qdbusxml2cpp根据输入文件中定义的接口,生成C++实现代码。
    qdbusxml2cpp可以辅助自动生成继承于QDBusAbstractAdaptor和QDBusAbstractInterface两个类的实现代码,用于进程通信服务端和客户端,简化了开发者的代码设计。
    qdbusxml2cpp使用语法如下:
    qdbusxml2cpp [options...] [xml-or-xml-file] [interfaces...]
    Options参数如下:
    -a filename:输出Adaptor代码到filename
    -c classname:使用classname作为生成类的类名
    -i filename:增加#include到输出
    -l classname:当生成Adaptor代码时,使用classname作为父类
    -m:在cpp文件中包含 #include "filename.moc"语句
    -N:不使用名称空间
    -p filename:生成Proxy代码到filename文件
    解析com.scorpio.test.xml文件,生成Adaptor类ValueAdaptor,文件名称为valueAdaptor.h、valueAdaptor.cpp命令行如下:
    qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
    解析com.scorpio.test.xml文件,生成Proxy类ComScorpioTestValueInterface,文件名称为testInterface.h、testInterface.cpp命令行如下:
    qdbusxml2cpp com.scorpio.test.xml -p testInterface

    五、QtDBus编程

    1、创建服务并注册对象

    test.h文件:

    #ifndef TEST_H
    #define TEST_H
    #include <QObject>
    
    class test: public QObject
    {
        Q_OBJECT
        //定义Interface名称为com.scorpio.test.value
        Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value")
    public:
        test(int value);
    
    public slots:
        int maxValue();
        int minValue();
        int value();
    private:
        int m_value;
    };
    
    #endif // TEST_H

    test.cpp文件:

    #include "test.h"
    
    test::test(int value)
    {
        m_value = value;
    }
    
    int test::maxValue()
    {
        return 100;
    }
    int test::minValue()
    {
        return 0;
    }
    int test::value()
    {
        return m_value;
    }

    main.cpp文件:

    #include <QCoreApplication>
    #include <QDBusConnection>
    #include <QDebug>
    #include <QDBusError>
    #include "test.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        //建立到session bus的连接
        QDBusConnection connection = QDBusConnection::sessionBus();
        //在session bus上注册名为com.scorpio.test的服务
        if(!connection.registerService("com.scorpio.test"))
        {
            qDebug() << "error:" << connection.lastError().message();
            exit(-1);
        }
        test object(60);
        //注册名为/test/objects的对象,把类Object所有槽函数导出为object的method
        connection.registerObject("/test/objects", &object,QDBusConnection::ExportAllSlots);
    
        return a.exec();
    }

    启动程序后,在命令行打开qdbusviewer,查看session bus。

    双击Method方法会调用该方法。

    2、通过QDBusMessage访问Service

    确保com.scorpio.test服务运行在总线上。
    编写一个控制台程序,使用消息访问com.scorpio.test服务。

    #include <QCoreApplication>
    #include <QDBusMessage>
    #include <QDBusConnection>
    #include <QDebug>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        //构造一个method_call消息,服务名称为:com.scorpio.test,对象路径为:/test/objects
        //接口名称为com.scorpio.test.value,method名称为value
        QDBusMessage message = QDBusMessage::createMethodCall("com.scorpio.test",
                               "/test/objects",
                               "com.scorpio.test.value",
                               "value");
        //发送消息
        QDBusMessage response = QDBusConnection::sessionBus().call(message);
        //判断method是否被正确返回
        if (response.type() == QDBusMessage::ReplyMessage)
        {
            //从返回参数获取返回值
            int value = response.arguments().takeFirst().toInt();
            qDebug() << QString("value =  %1").arg(value);
        }
        else
        {
            qDebug() << "value method called failed!";
        }
    
        return a.exec();
    }

    3、通过QDBusInterface 访问Service

    编写一个控制台程序,使用接口访问com.scorpio.test服务。

    #include <QCoreApplication>
    #include <QDBusMessage>
    #include <QDBusConnection>
    #include <QDBusReply>
    #include <QDBusInterface>
    #include <QDebug>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        // 创建QDBusInterface接口
        QDBusInterface interface("com.scorpio.test", "/test/objects",
                                 "com.scorpio.test.value",
                                 QDBusConnection::sessionBus());
        if (!interface.isValid())
        {
            qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
            exit(1);
        }
        //调用远程的value方法
        QDBusReply<int> reply = interface.call("value");
        if (reply.isValid())
        {
            int value = reply.value();
            qDebug() << QString("value =  %1").arg(value);
        }
        else
        {
            qDebug() << "value method called failed!";
        }
    
        return a.exec();
    }

    4、从D-Bus XML自动生成Proxy类

    Proxy Object提供了一种更加直观的方式来访问Service,如同调用本地对象的方法一样。 
    生成Proxy类的流程如下:
    A、使用工具qdbuscpp2xml从object.h生成XML文件;
    qdbuscpp2xml -M test.h -o com.scorpio.test.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.scorpio.test.value">
        <method name="maxValue">
          <arg type="i" direction="out"/>
        </method>
        <method name="minValue">
          <arg type="i" direction="out"/>
        </method>
        <method name="value">
          <arg type="i" direction="out"/>
        </method>
      </interface>
    </node>

    B、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
    qdbusxml2cpp com.scorpio.test.xml -p valueInterface
    生成两个文件:valueInterface.cpp和valueInterface.h
    valueInterface.h文件:

    /*
     * This file was generated by qdbusxml2cpp version 0.7
     * Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
     *
     * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
     *
     * This is an auto-generated file.
     * Do not edit! All changes made to it will be lost.
     */
    
    #ifndef TESTINTERFACE_H_1526737677
    #define TESTINTERFACE_H_1526737677
    
    #include <QtCore/QObject>
    #include <QtCore/QByteArray>
    #include <QtCore/QList>
    #include <QtCore/QMap>
    #include <QtCore/QString>
    #include <QtCore/QStringList>
    #include <QtCore/QVariant>
    #include <QtDBus/QtDBus>
    
    /*
     * Proxy class for interface com.scorpio.test.value
     */
    class ComScorpioTestValueInterface: public QDBusAbstractInterface
    {
        Q_OBJECT
    public:
        static inline const char *staticInterfaceName()
        { return "com.scorpio.test.value"; }
    
    public:
        ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
    
        ~ComScorpioTestValueInterface();
    
    public Q_SLOTS: // METHODS
        inline QDBusPendingReply<int> maxValue()
        {
            QList<QVariant> argumentList;
            return asyncCallWithArgumentList(QLatin1String("maxValue"), argumentList);
        }
    
        inline QDBusPendingReply<int> minValue()
        {
            QList<QVariant> argumentList;
            return asyncCallWithArgumentList(QLatin1String("minValue"), argumentList);
        }
    
        inline QDBusPendingReply<int> value()
        {
            QList<QVariant> argumentList;
            return asyncCallWithArgumentList(QLatin1String("value"), argumentList);
        }
    
    Q_SIGNALS: // SIGNALS
    };
    
    namespace com {
      namespace scorpio {
        namespace test {
          typedef ::ComScorpioTestValueInterface value;
        }
      }
    }
    #endif

    valueInterface.cpp文件:

    /*
     * This file was generated by qdbusxml2cpp version 0.7
     * Command line was: qdbusxml2cpp com.scorpio.test.xml -p testInterface
     *
     * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
     *
     * This is an auto-generated file.
     * This file may have been hand-edited. Look for HAND-EDIT comments
     * before re-generating it.
     */
    
    #include "testInterface.h"
    
    /*
     * Implementation of interface class ComScorpioTestValueInterface
     */
    
    ComScorpioTestValueInterface::ComScorpioTestValueInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
        : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
    {
    }
    
    ComScorpioTestValueInterface::~ComScorpioTestValueInterface()
    {
    }

    调用Proxy类访问Service如下:

    #include <QCoreApplication>
    #include <QDBusMessage>
    #include <QDBusConnection>
    #include <QDBusReply>
    #include <QDBusInterface>
    #include <QDebug>
    #include "testInterface.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        // 初始化自动生成的Proxy类com::scorpio::test::value
        com::scorpio::test::value test("com.scorpio.test",
                                       "/test/objects",
                                       QDBusConnection::sessionBus());
        // 调用value方法
        QDBusPendingReply<int> reply = test.value();
        //qdbusxml2cpp生成的Proxy类是采用异步的方式来传递Message,
        //所以需要调用waitForFinished来等到Message执行完成
        reply.waitForFinished();
        if (reply.isValid())
        {
            int value = reply.value();
            qDebug() << QString("value =  %1").arg(value);
        }
        else
        {
            qDebug() << "value method called failed!";
        }
    
        return a.exec();
    }

    5、使用Adapter注册Object

    可以直接把test类注册为消息总线上的一个Object,但QT4不推荐。QT4推荐使用Adapter来注册Object。
    大多数情况下,可能只需要把自定义的类里的方法有选择的发布到消息总线上,使用Adapter可以很方便的实现选择性发布。
    生成Adapter类的流程如下:
    A、使用工具 qdbuscpp2xml从test.h生成XML文件
    qdbuscpp2xml -M test.h -o com.scorpio.test.xml 
    B、编辑com.scorpio.test.xml,选择需要发布的method,不需要发布的删除。
    C、使用工具qdbusxml2cpp从XML文件生成继承自QDBusInterface的类
    qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
    生成两个文件:valueAdaptor.cpp和valueAdaptor.h
    valueAdaptor.h文件:

    /*
     * This file was generated by qdbusxml2cpp version 0.7
     * Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
     *
     * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
     *
     * This is an auto-generated file.
     * This file may have been hand-edited. Look for HAND-EDIT comments
     * before re-generating it.
     */
    
    #ifndef VALUEADAPTOR_H_1526742670
    #define VALUEADAPTOR_H_1526742670
    
    #include <QtCore/QObject>
    #include <QtDBus/QtDBus>
    #include "test.h"
    class QByteArray;
    template<class T> class QList;
    template<class Key, class Value> class QMap;
    class QString;
    class QStringList;
    class QVariant;
    
    /*
     * Adaptor class for interface com.scorpio.test.value
     */
    class ValueAdaptor: public QDBusAbstractAdaptor
    {
        Q_OBJECT
        Q_CLASSINFO("D-Bus Interface", "com.scorpio.test.value")
        Q_CLASSINFO("D-Bus Introspection", ""
    "  <interface name="com.scorpio.test.value">
    "
    "    <method name="maxValue">
    "
    "      <arg direction="out" type="i"/>
    "
    "    </method>
    "
    "    <method name="minValue">
    "
    "      <arg direction="out" type="i"/>
    "
    "    </method>
    "
    "  </interface>
    "
            "")
    public:
        ValueAdaptor(QObject *parent);
        virtual ~ValueAdaptor();
    
    public: // PROPERTIES
    public Q_SLOTS: // METHODS
        int maxValue();
        int minValue();
    Q_SIGNALS: // SIGNALS
    };
    
    #endif

    valueAdaptor.cpp文件:

    /*
     * This file was generated by qdbusxml2cpp version 0.7
     * Command line was: qdbusxml2cpp com.scorpio.test.xml -i test.h -a valueAdaptor
     *
     * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
     *
     * This is an auto-generated file.
     * Do not edit! All changes made to it will be lost.
     */
    
    #include "valueAdaptor.h"
    #include <QtCore/QMetaObject>
    #include <QtCore/QByteArray>
    #include <QtCore/QList>
    #include <QtCore/QMap>
    #include <QtCore/QString>
    #include <QtCore/QStringList>
    #include <QtCore/QVariant>
    
    /*
     * Implementation of adaptor class ValueAdaptor
     */
    
    ValueAdaptor::ValueAdaptor(QObject *parent)
        : QDBusAbstractAdaptor(parent)
    {
        // constructor
        setAutoRelaySignals(true);
    }
    
    ValueAdaptor::~ValueAdaptor()
    {
        // destructor
    }
    
    int ValueAdaptor::maxValue()
    {
        // handle method call com.scorpio.test.value.maxValue
        int out0;
        QMetaObject::invokeMethod(parent(), "maxValue", Q_RETURN_ARG(int, out0));
        return out0;
    }
    
    int ValueAdaptor::minValue()
    {
        // handle method call com.scorpio.test.value.minValue
        int out0;
        QMetaObject::invokeMethod(parent(), "minValue", Q_RETURN_ARG(int, out0));
        return out0;
    }

    调用Adaptor类注册Object对象如下:

    #include <QCoreApplication>
    #include <QDBusConnection>
    #include <QDebug>
    #include <QDBusError>
    #include "test.h"
    #include "valueAdaptor.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QDBusConnection connection = QDBusConnection::sessionBus();
        test object(60);
        //ValueAdaptor是qdbusxml2cpp生成的Adaptor类
        ValueAdaptor valueAdaptor(&object);
        if (!connection.registerService("com.scorpio.test"))
        {
            qDebug() << connection.lastError().message();
            exit(1);
        }
        connection.registerObject("/test/objects", &object);
        return a.exec();
    }

    使用qdbusviewer查看发布的method。

    6、自动启动Service

    D-Bus系统提供了一种机制可以在访问某个service时,自动把应用程序运行起来。
    需要在/usr/share/dbus-1/services下面建立com.scorpio.test.service文件,文件的内容如下:

    [D-BUS Service]
    Name=com.scorpio.test
    Exec=/path/to/scorpio/test

    在访问test的method前,不必手动运行应用程序。

    =================== End

  • 相关阅读:
    1058 A+B in Hogwarts (20)
    1036. Boys vs Girls (25)
    1035 Password (20)
    1027 Colors in Mars (20)
    1009. Product of Polynomials (25)
    1006. Sign In and Sign Out
    1005 Spell It Right (20)
    1046 Shortest Distance (20)
    ViewPager页面滑动,滑动到最后一页,再往后滑动则执行一个事件
    IIS7.0上传文件限制的解决方法
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/12715517.html
Copyright © 2011-2022 走看看