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\">\n" " <method name=\"maxValue\">\n" " <arg direction=\"out\" type=\"i\"/>\n" " </method>\n" " <method name=\"minValue\">\n" " <arg direction=\"out\" type=\"i\"/>\n" " </method>\n" " </interface>\n" "") 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM