「Qtの基礎 - D-Bus」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
286行目: 286行目:


== QtとD-Bus ==
== QtとD-Bus ==
==== Qt D-Busライブラリ ====
Qt D-Busライブラリは、D-Busプロトコルを使用してプロセス間通信を行うUnix向けライブラリであり、D-Busの基本APIをカプセル化して実装したものである。<br>
これは、Qtのシグナル・スロット機構で拡張されたインターフェイスを提供する。<br>
<br>
Qt D-Busライブラリを使用するには、<code>QtDBus</code>をインクルードする必要がある。<br>
<syntaxhighlight lang="c++">
#include <QtDBus>
</syntaxhighlight>
<br>
Qtプロジェクトファイルを使用する場合は、変数<code>QT</code>に<code>dbus</code>オプションを追加する必要がある。<br>
QT += dbus
<br>
==== QtとD-Busのデータ型 ====
QtとD-Busは、<code>QDBusArgument</code>クラスを通して、ネイティブな型をサポートしている。<br>
また、<code>QDBusArgument</code>クラスは、ネイティブな型の他に非ネイティブ型である<code>QStringList</code>クラスと<code>QByteArray</code>クラスもサポートする。<br>
<center>
{| class="wikitable" | style="background-color:#fefefe;"
|-
! style="background-color:#66CCFF;" | Qtのデータ型
! style="background-color:#66CCFF;" | D-Busのデータ型
|-
| uchar || BYTE
|-
| bool || BOOLEAN
|-
| short || INT16
|-
| ushort || UINT16
|-
| int || INT32
|-
| uint || UINT32
|-
| qlonglong || INT64
|-
| qulonglong || UNIT64
|-
| double || DOUBLE
|-
| QString || STRING
|-
| QDBusVariant || VARIANT
|-
| QDBusObjectPath || OBJECT_PATH
|-
| QDBusSignature || SIGNATURE
|}
</center>
<br>
==== qdbuscpp2xmlコマンド ====
<code>qdbuscpp2xml</code>コマンドは、<code>QObject</code>クラスを継承したクラスのヘッダファイル等をパースして、D-Busインターフェースファイルを生成する。<br>
スロットの引数が<code>const</code>で宣言されている場合は入力、非<code>const</code>の場合は出力とみなされることがある。<br>
qdbuscpp2xml <入力オプション> <QObjectクラスを継承したクラスを定義したヘッダファイル> <出力オプション> <生成するD-Busインターフェースファイル名>
例. qdbuscpp2xml -M SampleHelper.h -O org.qt.sample.xml
<br>
qdbuscpp2xmlコマンドのオプションを、以下に示す。<br>
* -p または -s または -m
*: スクリプト化されたアトリビュート(<code>-p</code>オプション)、シグナル(<code>-s</code>オプション)、メソッド(スロット関数)(<code>-m</code>オプション)のみがパースされる。
* -P または -S または -M
*: 全てのアトリビュート(<code>-P</code>オプション)、シグナル(<code>-S</code>オプション)、メソッド(スロット関数)(<code>-M</code>オプション)を解析する。
* -a
*: スクリプト化された内容を全て出力する。(<code>-psm</code>オプションと等価)
* -A
*: 全てのコンテンツを出力する。(<code>-PSM</code>オプションと等価)
* -o <生成するD-Busインターフェースファイル名> または -O <生成するD-Busインターフェースファイル名>
*: D-Busインターフェースファイルを生成する。
<br>
==== サンプルコード ====
以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。<br>
以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。<br>
# セッションバスへの接続を取得する。
# セッションバスへの接続を取得する。

2022年12月19日 (月) 02:08時点における版

概要

D-Busは、オープンソースのプロセス間通信(IPC:Inter Process Communication)機構であり、freedesktop.orgプロジェクトの一部である。
IPCとは、1台のコンピュータ上で動作する複数のプログラムの間で情報を交換するシステムのことである。

IPCには、パイプ、名前付きパイプ、シグナル、共有メモリ、Unixソケット、ループバックソケット等がある。
D-Busもリンク層はUnixソケットで動作しているが、手順とフォーマット(プレゼンテーション層)が既定されていることが、「生の」Unixソケットとは異なる。

開発当初はGNOME等のGUIの制御を目的としていが、今日では、GUIに限らず幅広いソフトウェアで使用されており、
デスクトップの通知、メディアプレーヤー制御、XDGポータル等、多くのfreedesktop.org標準がD-Busをベースに構築されている。

IPCとは、あるプロセスから別のプロセスへ情報を取得する方法を説明するために使用することができる。
これは、データの交換、メソッドの呼び出し、イベントのリスニング等がある。

  • デスクトップにおけるIPCの使用例
    • スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)
    • 集中型サービスへのアクセスの提供
    • 協調型ソフトウェアの複数のインスタンス間の調整


  • D-Busの使用例
    • freedesktop.orgのnotification仕様
      これは、ソフトウェアは通知を中央サーバ(Plasma等)に送信して、中央サーバは通知を表示して、通知が閉じられたりアクションが実行されたりといったイベントを送り返すものである。


  • IPCの他の使用例
    • ユニークなソフトウェアのサポート
      これは、ソフトウェアの起動時に、まず、同じソフトウェアの他の実行中のインスタンスを確認して、
      もし存在すれば、IPCを介して実行中のインスタンスにメッセージを送信して、自分自身を表示して終了させる。


D-Busは、言語やツールキットに囚われないため、あらゆるプロバイダのソフトウェアやサービスが相互作用することができる。
デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。

Qtは、D-Busと対話するためのクラスとツールのセットを提供している。


D-Busの基礎

D-Busとは

D-Busは、メッセージオブジェクトに届ける仕組みである。
メッセージはデータであり、オブジェクトはデータの受け手であるプログラムのことである。

D-Busのメッセージには、手続き呼び出し(METHOD_CALL)、手続きの戻り値(METHOD_RETURN)、エラー(ERROR)、シグナル(SIGNAL)の4種類がある。

手続き呼び出し(METHOD_CALL)と手続きの戻り値(METHOD_RETURN)はペアで使用されており、
他のプロセスが提供するサービス機能を関数の呼び出しのように使用することができるという一種のRPC(Remote Procedure Call)のようなものとして扱うことができる。

ただし、D-Busは同一PC上のIPCに特化しており、RPCのようなネットワークを介した異なるPC間の通信は原則としてサポートしていない。

シグナル(SIGNAL)は、片方向の一斉通知として使用することができ、ネットワークが落ちた時、USBデバイスの検出、再起動の要請等の通知メカニズムとして多用されている。

オブジェクトは、サービスに内包された操作対象を指す名前である。

D-Busサービス名とは

D-Busにおけるサービス名は、サービス提供プロセスがD-Busサーバに接続する時に登録する名前のことである。
"org.freedesktop.DBus"や"org.bluez"等のように、.(コロン)区切りの文字列(慣習として、サービスプログラム開発元の公式ドメイン)が使用される。

※注意
D-Busサービス名は、バス名コネクション名等、異なる名前で呼ばれる場合もある。
D-Busの仕様としては、バス名が正式名称であり、名前を明示的に指定したバス名をサービス名と呼ぶこともある。

プロセスはサービス名を明示せずに、D-Busサーバに接続することも可能である。
この場合、":1.128"のようにD-Busサーバが適当に生成した数字の羅列が割り当てられる。

D-Busオブジェクトとは

オブジェクトは、バス名(D-Busサービス名)に対して送られるメッセージに含まれる、そのバス名(D-Busサービス名)内におけるメッセージの宛先である。
/から始まり、/(スラッシュ)区切りの文字列で表記されており、これを、オブジェクトパス、あるいは、パス名パスと呼ぶこともある。

慣習として、パス名は、サービス名の./に置き換えた接頭辞(プレフィックス)を持たせる。

例えば、BlueZのbluetoothデーモンは、サービス名org.bluezを持つが、BlueZのオブジェクトは/org/bluez/hci0のような名前である。
HCIインターフェースを複数持つシステムの場合、/org/bluez/hci1、/org/bluez/hci2のようなオブジェクト名により、どのインターフェースに対するメッセージかを識別することになる。

サービスが持つオブジェクトの一覧は、イントロスペクト(Introspect)という機能により検索することができる。
例えば、BlueZ 5のサービスに対して、dbus-sendコマンドを実行する。

dbus-send --print-reply --system --dest=org.bluez / --type=method_call org.freedesktop.DBus.Introspectable.Introspect

# 出力例

...略
</interface><node name="org"/></node>"


上記の出力例のように、出力された<node name="org"/>の項目は、以下に示す内容を意味する。

  1. バス名(D-Busサービス名)org.bluezのD-Busオブジェクト/に対して、
  2. 手続きメッセージorg.freedesktop.DBus.Introspectable.Introspectを送信した時、
  3. /の下にはorgというオブジェクトがあるというメッセージを受信した。


D-Busインターフェース (D-Busメッセージ)

上記セクションにあるような"org.freedesktop.DBus.Introspectable.Introspect"を手続きメッセージの例として扱っている。
これは、freedesktop.orgで既定された標準メッセージの一部であるが、各サービスごとに独自のメッセージを実装することもできる。

例えば、BlueZ 5において、HCIインターフェースを検索可能状態にするメッセージは、以下に示すようなものになる。

dbus-send --print-reply \
--system \
--dest=org.bluez \
/org/bluez/hci0 \
--type=method_call \
org.bluez.Adapter1.StartDiscovery


D-Busインターフェースは、オブジェクト指向の考えに基づいており、同じD-Busインターフェースに属する手続きは、異なるD-Busオブジェクト(サービス)間でも同じ機能として動作する。

例えば、イントロスペクト機能のorg.freedesktop.DBus.Introspectable.IntrospectというD-Busインターフェースは、
org.freedesktop.DBusサービスの/オブジェクトに対しても、org.bluezサービスの/org/bluez/hci0オブジェクトに対しても、同様に動作する。

D-Busプロパティ

org.freedesktop.DBusの標準インターフェースとして、D-Busプロパティがある。(プロパティと呼ぶこともある)
D-Busプロパティは、名前 + 値のセットであり、これにより、D-Busオブジェクトの持つ状態や設定等を共通の枠組みで扱うことができる。

D-Busプロパティのインターフェースはorg.freedesktop.DBus.Propertiesであり、GetメソッドやSetメソッド等が定義されている。
以下の例では、org.bluezサービス、/org/bluez/hci0オブジェクトの持つプロパティの一覧が表示される。

dbus-send --print-reply \
--system \            # D-Busのタイプ
--dest=org.bluez \    # バス名(D-Busサービス名)
/org/bluez/hci0 \     # D-Busオブジェクト名
--type=method_call \  # メッセージタイプ
org.freedesktop.DBus.Properties.GetAll \  # D-Busインターフェース名(org.freedesktop.DBus.Properties)とメソッド名(GetAll)
string:org.bluez.Adapter1                 # メソッドの引数



バス

D-Busは、ソフトウェアが相互に通信するため、複数のメッセージバスを提供する。

各バスには独自の接続機能があり、異なるカテゴリーのメッセージを分離することができる。

あるバスで送信されたメッセージは他のバスからアクセスできないが、同じバスに接続されたソフトウェアは全て互いに通信することができる。
任意のバスに複数のソフトウェアを同時に接続することができ、また、1つのソフトウェアが複数のバスに同時に接続することも可能である。 これにより、バスごとに異なるセキュリティポリシーを適用できると共に、グローバルメッセージとローカルメッセージの両方を効率的に共有することができる。

D-Busは、2つの定義済みバス(システムバスセッションバス)を予め用意されており、一般的なD-Busの使用方法をほぼカバーすることができる。

  • システムバス
    ハードウェア管理等のシステムグローバルサービスに使用される。
    これはユーザー間で共有され、通常、厳格なセキュリティ・ポリシーが付属しています。

  • セッションバス
    各デスクトップセッション(ログインしているユーザ等)には、セッションバスが存在する。
    これは、GUIソフトウェア等が最も頻繁に使用する傾向がある。


さらに、ソフトウェアは、必要に応じて複数の独自のバスを作成することができる。


メッセージ

メッセージは、バスにおける通信の基本単位である。

TCP/IPのパケットと同様に、バスでやり取りされる情報は、全てメッセージで行われる。
しかし、ネットワークのパケットとは異なり、D-Busの各メッセージには、送受信されるデータ1式が含まれていることが保証されている。

メッセージには、送信されるデータだけでなく、送信者と受信者が誰であるかも記録されており、適切なルーティングを可能にする。
メッセージは、メソッドコール、シグナルエミッション、メソッドの戻り値であり、エラー情報も含むことがある。


名前空間とアドレス

複数のソフトウェアが同一のバス上に存在し、1つのソフトウェアが複数のオブジェクトを提供してメッセージを送信することができるため、
特定の住宅やオフィスを一意に識別するのと同様に、任意のバス上の任意のオブジェクトを効果的かつ一意にアドレス指定する手段を持つことが必要である。

インターフェース、サービス、オブジェクト名の3つの情報を組み合わせることにより、バス上の任意のオブジェクトに一意のアドレスを作成することができる。

インターフェイス

インターフェイスは、バス上に公表される呼び出し可能なメソッドとシグナルのセットである。

インターフェースは、メッセージを渡すソフトウェアの間で、インターフェースの名前、パラメータ(もしあれば)、戻り値(もしあれば)を定義する"契約"を提供する。

これらのメソッドは、インターフェイスを使用しているソフトウェアのメソッドやAPIに1対1で直接マッピングされることもある。(直接マッピングされないこともある)
これにより、複数のソフトウェアが、内部の実装に関係なく、類似または同一のインターフェイスを提供することができ、
一方で、ソフトウェアは、ソフトウェアの内部設計を気にすることなく、これらのインターフェイスを使用できるようになる。

インターフェイスは、文書化やコードの再利用を目的として、XMLで記述する。
ユーザや開発者は、インターフェースのXML記述を参照できるだけでなく、開発者はXMLから自動生成されたクラスを使用することができる。
このため、D-Busの使用は非常に簡単で、エラーが発生しにくくなる。(例. コンパイラがコンパイル時にメッセージの構文を確認することができる)

サービス

サービスとは、ソフトウェアとバスの接続を表すものである。

ここでいうサービスとは、D-Busの用語でいうバス名に相当するものである。
(バス名という用語は、バス上の接続名であり、バスの名前ではない。そのため、Qtのドキュメントで記載されているように、サービスという用語を使用する)

これらは、複数のコンポーネントの名前空間を必要とする他の多くのシステムで見られるように、"逆ドメイン名"アプローチを使用することにより、一意に保たれる。
KDEプロジェクトのソフトウェアが提供する多くのサービスでは、サービス名にorg.kdeというプレフィックスを使用している。
そのため、セッションバスにおいて、org.kde.screensaverが宣伝されているのを見掛けるかもしれない。

サービス名には、開発者の組織やソフトウェアのドメイン名を使用することを推奨する。
例えば、開発者のドメインがawesomeapps.org、ソフトウェアの名前がwickedwidgetの場合、バス上のサービス名はorg.awesomeapps.wickedwidgetとなる。

ソフトウェアが複数のバスに接続している場合、または、同一のソフトウェアの複数のインスタンスが同時にアクティブになっている場合は、接続ごとに一意なサービス名を使用する必要がある。
多くの場合、プロセスIDをサービス名に付加することでこれを実現する。

オブジェクト

ソフトウェアは、バス上の複数のオブジェクトへのアクセスを宣伝する可能性が高い。

オブジェクトとサービスの間のこの多対1の関係は、アドレスにパスコンポーネントを提供することで対応される。

サービスに関連付けられた各パスは、異なる一意のオブジェクトを表す。(例. /MainInterface、/Documents/Doc1)
実際のパス構造は完全に任意であり、どのようなパスにするかは、サービスを提供するソフトウェア次第である。
これらのパスは、他のソフトウェアにメッセージを送信するソフトウェアのために、オブジェクトを識別し、論理的にグループ化する方法を提供する。

一部のライブラリは、オブジェクトを適切に名前空間化するために、オブジェクトパスの先頭に"リバースドメイン"を付けてエクスポートする。
これは、任意のサービスに参加するライブラリやプラグインではよくあることで、そのため、ソフトウェアや他のコンポーネントによりエクスポートされたオブジェクトとの全ての衝突を避けなければならない。
しかし、KDEソフトウェアやライブラリでは、この方法は使用されていない。

オブジェクトはインターフェースへのアクセスを提供しており、オブジェクトは同時に複数のインタフェースへのアクセスを提供することができる。

アドレスの例

D-Busメッセージには、上記で記載したコンポーネントで構成されるアドレスが含まれており、正しいソフトウェア、オブジェクト、メソッドコールにルーティングされる。

# 例. KRunnerのアドレス 
org.kde.krunner /App org.kde.krunner.App.display

org.kde.krunner     : サービス
/App                : オブジェクトへのパス
org.kde.krunner.App : オブジェクトがエクスポートするインターフェース
display             : インターフェース内のメソッド


もし、/Appオブジェクトがorg.kde.krunner.Appインターフェースを提供するのみの場合(または、実装するサービスの中で表示メソッドが一意の場合)、これはアドレスとして同様に機能する。

org.kde.krunner /App display


このようにして、可能性のあるそれぞれの宛先を一意かつ確実にアドレスで指定することができる。


メソッドとシグナル

バス上の任意のエンドポイントをアドレス指定する方法を使用して、メッセージを送受信する場合の可能性を検討する。

メソッド

受信側のソフトウェアにおいて、メソッド(関数)を実行するために送信されるメッセージのことである。

メソッドの呼び出しに失敗した場合(アドレスの間違い、または、要求されたソフトウェアが動作していない等の理由でメソッドが利用できない場合)、呼び出し側のソフトウェアにエラーが返る。

メソッドの呼び出しに成功した場合、呼び出し元のソフトウェアにオプションの戻り値が返る。(戻り値が提供されない場合でも、成功メッセージが返る)
このラウンドトリップにはオーバーヘッドがあり、パフォーマンスが重要なコードではこれを念頭に置くことが重要である。

メソッド呼び出しは、常に呼び出し元のソフトウェアによって開始されて、結果として生じるメッセージは正確に1つの送信元アドレスと1つの送信先アドレスを持つ。

シグナル

シグナルはメソッドコールに似ているが、"逆方向"に発生すること、単一の宛先に縛られないことが特徴である。

シグナルは、インターフェイスをエクスポートしているソフトウェアから発信されて、同一バス上のどのソフトウェアでも利用できる。
これにより、ソフトウェアは、状態の変化やその他のイベントを、それらの変化を追跡するソフトウェアに自発的に知らせることができる。

これは、Qtのシグナルとスロットの仕組みに似ており、これは、同じ機能のシステム版である。


D-Busコマンド

以下の例では、Linuxでのシャットダウン、再起動、ログアウト等のD-Busサービスの一覧を取得している。

dbus-send --system --print-reply=literal \
--dest=org.freedesktop.login1 \
/org/freedesktop/login1 \
--type=method_call \
org.freedesktop.DBus.Introspectable.Introspect


以下の例では、D-Busサービスを使用してシャットダウンおよび再起動を実行している。

# シャットダウン
dbus-send --print-reply=literal \
--system \                                   # バスタイプ
--dest=org.freedesktop.login1 \              # バス名(D-Busサービス名)
/org/freedesktop/login1 \                    # D-Busオブジェクト名(オブジェクトパス)
                                             # バス名(D-Busサービス)に送信するメッセージに含まれるそのバス名(D-Busサービス)内におけるメッセージの宛先
"org.freedesktop.login1.Manager.PowerOff" \  # D-Busインターフェース名(org.freedesktop.login1.Manager)とD-Busプロパティ名(PowerOff)
boolean:true                                 # プロパティのインターフェース名(関数の引数の型(boolean)と値(true))

# 再起動
dbus-send --print-reply=literal \
--system                                   # バスタイプ
--dest=org.freedesktop.login1 \            # バス名(D-Busサービス名)
/org/freedesktop/login1 \                  # D-Busオブジェクト名(オブジェクトパス)
                                           # バス名(D-Busサービス)に送信するメッセージに含まれるそのバス名(D-Busサービス)内におけるメッセージの宛先
"org.freedesktop.login1.Manager.Reboot" \  # D-Busインターフェース名(org.freedesktop.login1.Manager)とD-Busプロパティ名(Reboot)
boolean:true                               # D-Busプロパティのインターフェース名(関数の引数の型(boolean)と値(true))



便利なツール

D-Busのバスの検索やD-Busを使用したソフトウェアの開発には、いくつかのエンドユーザ向けの便利なツールが存在する。

qdbus

qdbusコマンドは、コマンドラインツールであり、与えられたバス上のサービス、オブジェクト、インタフェースを表示したり、バス上の与えられたアドレスにメッセージを送信するために使用できる。
これは、セッションバスおよびシステムバスの両方を検索するために使用することができる。

--systemオプションを付加する場合は、qdbusはシステムバスに接続する。
--systemオプションを付加しない場合は、セッションバスを使用する。

qdbusコマンドは、与えられた引数を、与えられたオブジェクトに渡すためのアドレス、および、パラメータ(存在する場合)として使用する。
もし、完全なアドレスが与えられなかった場合、バス上のその地点から利用可能な全てのオブジェクトを表示する。

例えば、アドレスが提供されない場合は、利用可能なサービスのリストが表示される。
サービス名を指定する場合、オブジェクトのパスが提供される。
パスを指定する場合、全てのインターフェースの全てのメソッドが表示される。

このように、非常に簡単にバス上のオブジェクトを検索して操作することができる。

qdbusviewer

qdbusviewerは、Qtで作成されたGUIソフトウェアであり、qdbusコマンドがCUIから提供する機能と基本的に同一のものをGUIとして提供している。
qdbusviewerは、Qt本体と同梱されており、オブジェクトアドレス等のD-Busの基本概念に慣れていれば、誰でも簡単に使用できる。


QtとD-Bus

Qt D-Busライブラリ

Qt D-Busライブラリは、D-Busプロトコルを使用してプロセス間通信を行うUnix向けライブラリであり、D-Busの基本APIをカプセル化して実装したものである。
これは、Qtのシグナル・スロット機構で拡張されたインターフェイスを提供する。

Qt D-Busライブラリを使用するには、QtDBusをインクルードする必要がある。

 #include <QtDBus>


Qtプロジェクトファイルを使用する場合は、変数QTdbusオプションを追加する必要がある。

QT += dbus


QtとD-Busのデータ型

QtとD-Busは、QDBusArgumentクラスを通して、ネイティブな型をサポートしている。
また、QDBusArgumentクラスは、ネイティブな型の他に非ネイティブ型であるQStringListクラスとQByteArrayクラスもサポートする。

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


qdbuscpp2xmlコマンド

qdbuscpp2xmlコマンドは、QObjectクラスを継承したクラスのヘッダファイル等をパースして、D-Busインターフェースファイルを生成する。
スロットの引数がconstで宣言されている場合は入力、非constの場合は出力とみなされることがある。

qdbuscpp2xml <入力オプション> <QObjectクラスを継承したクラスを定義したヘッダファイル> <出力オプション> <生成するD-Busインターフェースファイル名>
例. qdbuscpp2xml -M SampleHelper.h -O org.qt.sample.xml


qdbuscpp2xmlコマンドのオプションを、以下に示す。

  • -p または -s または -m
    スクリプト化されたアトリビュート(-pオプション)、シグナル(-sオプション)、メソッド(スロット関数)(-mオプション)のみがパースされる。
  • -P または -S または -M
    全てのアトリビュート(-Pオプション)、シグナル(-Sオプション)、メソッド(スロット関数)(-Mオプション)を解析する。
  • -a
    スクリプト化された内容を全て出力する。(-psmオプションと等価)
  • -A
    全てのコンテンツを出力する。(-PSMオプションと等価)
  • -o <生成するD-Busインターフェースファイル名> または -O <生成するD-Busインターフェースファイル名>
    D-Busインターフェースファイルを生成する。


サンプルコード

以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。

  1. セッションバスへの接続を取得する。
  2. セッションバスへのD-Busインターフェースを作成する。
  3. D-Busインターフェイス名でのサービス登録する。
  4. バス上に誰が存在しているかを確認する。
  5. pingリクエストを受信するために登録する。
  6. Pingメッセージの送信する。
  7. 受信したPingメッセージの表示する。


受信したPingメッセージの表示以外は、doItメソッドで行う。
Pingメッセージは、pingReceivedメソッドで処理される。

プログラムを実行すると、セッションバス上に既に多くのサービスが表示されるが、"-- end --"の付近にPingリクエストに応答していることがわかる。

 #include <QDBusConnection>
 #include <QDBusConnectionInterface>
 #include <QTimer>
 #include "MyObject.h"
 
 MyObject::MyObject(QObject *parent) : QObject(parent)
 {
    QTimer::singleShot(10, this, &MyObject::doIt);
 }
 
 void MyObject::doIt()
 {
    /// [1] セッションバスへの接続を取得
    QDBusConnection bus = QDBusConnection::sessionBus();
 
    /// [2] セッションバスへD-Busインターフェースを作成
    QDBusConnectionInterface *busIF = bus.interface();
 
    /// [3] D-Busインターフェイス名でのサービス登録
    QString ifName = "com.packt.bigproject";
    busIF->registerService(ifName,
                           QDBusConnectionInterface::ReplaceExistingService,
                           QDBusConnectionInterface::AllowReplacement);
 
    /// [4] バス上に誰が存在しているかを確認
    QDBusReply<QStringList> serviceNames = busIF->registeredServiceNames();
    qDebug() << bus.name() << "knows the following Services:" << serviceNames.value();
 
    /// [5] pingリクエストを受信するために登録 (QObject::connectメソッドと似ている)
    QString service = "";
    QString path = "";
    QString name = "ping";
    bus.connect(service, path, ifName, name, this, SLOT(pingReceived(QString)));
 
    /// [6] Pingメッセージを送信
    QDBusMessage msg = QDBusMessage::createSignal("/", ifName, name);
    msg << "Hello World!";
    bus.send(msg);
 
    /// 5秒以内にもう1度行う
    QTimer::singleShot(5000, this, &MyObject::doIt);
 }
 
 void MyObject::pingReceived(QString msg)
 {
    /// [8] 受信したPingメッセージの表示
    qDebug() << __FUNCTION__ << "Ping:" << msg;
 }


サンプルコードの詳細を知りたい場合は、以下に示すGithubを参照すること。
https://github.com/PacktPublishing/Hands-On-Embedded-Programming- with-Qt/blob/master/Chapter10/DBusBruteForce/MyObject.cpp


Qtプロジェクト

Qtプロジェクトにおいて、Qt Creator付属のqdbusxml2cppコマンドを実行することにより、D-Busインターフェースのアダプタクラスを生成する。
生成されるファイルは、D-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルである。

# mocファイルはインクルードしない場合
qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>
例. qdbusxml2cpp -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml

# mocファイルもインクルードする場合
qdbusxml2cpp -m -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>
例. qdbusxml2cpp -m -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml


生成されたファイルをQtプロジェクトに指定およびインクルードする。

または、Qtプロジェクトファイルにおいて、qdbusxml2cppコマンドを自動実行する設定を記述してもよい。

system(qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>)



CMakeプロジェクト

CMakeプロジェクトにおいて、qt_add_dbus_adaptorオプションを指定することにより、D-Busインターフェースのアダプタクラスを生成する。
qt_add_dbus_adaptorコマンドは、Qt D-Bus XMLコンパイラ(qdbusxml2cpp)のアダプタモードでの呼び出しを設定する。

第2引数で指定したD-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルを生成する。
生成されたファイルのパスが第1引数に追加される。

第3引数には、D-Busインターフェースに基づく親クラスのヘッダファイル名を指定する。
生成されるアダプタを実装したソースコードには、#include "<第3引数で指定したヘッダファイル名>"としてインクルードされる。

第4引数には、第3引数のクラス名(D-Busインターフェースに基づく親クラス名)を指定する。(省略可能)
第5引数には、生成するヘッダファイル名(拡張子.hは不要)を指定する。(省略可能)
第6引数には、生成するアダプタのクラス名を指定する。(省略可能)

第4引数から第6引数までを省略する場合、親クラス名、ヘッダファイル名、クラス名は、第3引数の指定値から自動的に生成される。

 qt_add_dbus_adaptor(
   <任意の変数名>
   <D-Busインタフェースファイル(XML形式)>
   <親クラスのヘッダファイル名>
   [第3引数のクラス名 (省略可能)]
   [生成するヘッダファイル名(拡張子.hは不要) (省略可能)]
   [生成するアダプタのクラス名 (省略可能)]
 )