📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
(同じ利用者による、間の15版が非表示)
29行目: 29行目:
デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。<br>
デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。<br>
<br>
<br>
Qtは、D-Busと対話するためのクラスとツールのセットを提供している。<br>
また、Qtは、D-Busと対話するためのクラスとツールのセットを提供している。<br>
<br><br>
 
== D-Busの基礎 ==
==== D-Busとは ====
D-Busは、<u>メッセージ</u>を<u>オブジェクト</u>に届ける仕組みである。<br>
メッセージはデータであり、オブジェクトはデータの受け手であるプログラムのことである。<br>
<br>
D-Busのメッセージには、手続き呼び出し(METHOD_CALL)、手続きの戻り値(METHOD_RETURN)、エラー(ERROR)、シグナル(SIGNAL)の4種類がある。<br>
<br>
手続き呼び出し(METHOD_CALL)と手続きの戻り値(METHOD_RETURN)はペアで使用されており、<br>
<u>他のプロセスが提供するサービス機能を関数の呼び出しのように使用することができる</u>という一種のRPC(Remote Procedure Call)のようなものとして扱うことができる。<br>
<br>
ただし、D-Busは同一PC上のIPCに特化しており、RPCのようなネットワークを介した異なるPC間の通信は原則としてサポートしていない。<br>
<br>
シグナル(SIGNAL)は、片方向の一斉通知として使用することができ、ネットワークが落ちた時、USBデバイスの検出、再起動の要請等の通知メカニズムとして多用されている。<br>
<br>
オブジェクトは、<u>サービス</u>に内包された<u>操作対象を指す名前</u>である。<br>
<br>
==== D-Busサービス名とは ====
D-Busにおけるサービス名は、サービス提供プロセスがD-Busサーバに接続する時に登録する名前のことである。<br>
"org.freedesktop.DBus"や"org.bluez"等のように、<code>.(コロン)</code>区切りの文字列(慣習として、サービスプログラム開発元の公式ドメイン)が使用される。<br>
<br>
※注意<br>
D-Busサービス名は、<u>バス名</u>、<u>コネクション名</u>等、異なる名前で呼ばれる場合もある。<br>
D-Busの仕様としては、<u>バス名</u>が正式名称であり、名前を明示的に指定したバス名を<u>サービス名</u>と呼ぶこともある。<br>
<br>
プロセスはサービス名を明示せずに、D-Busサーバに接続することも可能である。<br>
この場合、":1.128"のようにD-Busサーバが適当に生成した数字の羅列が割り当てられる。<br>
<br>
==== D-Busオブジェクトとは ====
オブジェクトは、バス名(D-Busサービス名)に対して送られるメッセージに含まれる、そのバス名(D-Busサービス名)内におけるメッセージの宛先である。<br>
<code>/</code>から始まり、<code>/(スラッシュ)</code>区切りの文字列で表記されており、これを、<u>オブジェクトパス</u>、あるいは、<u>パス名</u>、<u>パス</u>と呼ぶこともある。<br>
<br>
慣習として、パス名は、サービス名の<code>.</code>を<code>/</code>に置き換えた接頭辞(プレフィックス)を持たせる。<br>
<br>
例えば、BlueZのbluetoothデーモンは、サービス名org.bluezを持つが、BlueZのオブジェクトは/org/bluez/hci0のような名前である。<br>
HCIインターフェースを複数持つシステムの場合、/org/bluez/hci1、/org/bluez/hci2のようなオブジェクト名により、どのインターフェースに対するメッセージかを識別することになる。<br>
<br>
サービスが持つオブジェクトの一覧は、イントロスペクト(Introspect)という機能により検索することができる。<br>
例えば、BlueZ 5のサービスに対して、<code>dbus-send</code>コマンドを実行する。<br>
dbus-send --print-reply --system --dest=org.bluez / --type=method_call org.freedesktop.DBus.Introspectable.Introspect
# 出力例
...略
</interface><node name="org"/></node>"
<br>
上記の出力例のように、出力された<node name="org"/>の項目は、以下に示す内容を意味する。<br>
# バス名(D-Busサービス名)<code>org.bluez</code>のD-Busオブジェクト<code>/</code>に対して、
# 手続きメッセージ<code>org.freedesktop.DBus.Introspectable.Introspect</code>を送信した時、
# <code>/</code>の下には<code>org</code>というオブジェクトがあるというメッセージを受信した。
<br>
==== D-Busインターフェース (D-Busメッセージ) ====
上記セクションにあるような"org.freedesktop.DBus.Introspectable.Introspect"を手続きメッセージの例として扱っている。<br>
これは、freedesktop.orgで既定された標準メッセージの一部であるが、各サービスごとに独自のメッセージを実装することもできる。<br>
<br>
例えば、BlueZ 5において、HCIインターフェースを検索可能状態にするメッセージは、以下に示すようなものになる。<br>
dbus-send --print-reply \
--system \
--dest=org.bluez \
/org/bluez/hci0 \
--type=method_call \
org.bluez.Adapter1.StartDiscovery
<br>
D-Busインターフェースは、オブジェクト指向の考えに基づいており、同じD-Busインターフェースに属する手続きは、異なるD-Busオブジェクト(サービス)間でも同じ機能として動作する。<br>
<br>
例えば、イントロスペクト機能のorg.freedesktop.DBus.Introspectable.IntrospectというD-Busインターフェースは、<br>
org.freedesktop.DBusサービスの/オブジェクトに対しても、org.bluezサービスの/org/bluez/hci0オブジェクトに対しても、同様に動作する。<br>
<br>
==== D-Busプロパティ ====
org.freedesktop.DBusの標準インターフェースとして、D-Busプロパティがある。(<u>プロパティ</u>と呼ぶこともある)<br>
D-Busプロパティは、<u>名前 + 値</u>のセットであり、これにより、D-Busオブジェクトの持つ状態や設定等を共通の枠組みで扱うことができる。<br>
<br>
D-Busプロパティのインターフェースはorg.freedesktop.DBus.Propertiesであり、<code>Get</code>メソッドや<code>Set</code>メソッド等が定義されている。<br>
以下の例では、org.bluezサービス、/org/bluez/hci0オブジェクトの持つプロパティの一覧が表示される。<br>
dbus-send --print-reply      \                      # 相手からの応答を出力
          --system          \                      # D-Busのタイプ (システムバスまたはセッションバス)
          --dest=org.bluez  \                      # バス名 (D-Busサービス名)
          /org/bluez/hci0    \                      # D-Busオブジェクト名
          --type=method_call \                      # メッセージタイプ
                                                    # メソッドの場合は<code>method_call</code>、シグナルの場合は<code>signal</code>を指定する
                                                    # 省略した場合は、<code>signal</code>を指定したことになる
          org.freedesktop.DBus.Properties.GetAll \  # D-Busインターフェース名(org.freedesktop.DBus.Properties)とメソッド名(GetAll)
          string:org.bluez.Adapter1                # メソッドの引数
<br><br>
 
== バス ==
D-Busは、ソフトウェアが相互に通信するため、複数のメッセージバスを提供する。<br>
<br>
各バスには独自の接続機能があり、異なるカテゴリーのメッセージを分離することができる。<br>
<br>
あるバスで送信されたメッセージは他のバスからアクセスできないが、同じバスに接続されたソフトウェアは全て互いに通信することができる。<br>
任意のバスに複数のソフトウェアを同時に接続することができ、また、1つのソフトウェアが複数のバスに同時に接続することも可能である。
これにより、バスごとに異なるセキュリティポリシーを適用できると共に、グローバルメッセージとローカルメッセージの両方を効率的に共有することができる。<br>
<br>
D-Busは、2つの定義済みバス(<u>システムバス</u>と<u>セッションバス</u>)を予め用意されており、一般的なD-Busの使用方法をほぼカバーすることができる。<br>
* システムバス
*: ハードウェア管理等のシステムグローバルサービスに使用される。
*: これはユーザー間で共有され、通常、厳格なセキュリティ・ポリシーが付属しています。
*: <br>
* セッションバス
*: 各デスクトップセッション(ログインしているユーザ等)には、セッションバスが存在する。
*: これは、GUIソフトウェア等が最も頻繁に使用する傾向がある。
<br>
さらに、ソフトウェアは、必要に応じて複数の独自のバスを作成することができる。<br>
<br><br>
 
== メッセージ ==
メッセージは、バスにおける通信の基本単位である。<br>
<br>
TCP/IPのパケットと同様に、バスでやり取りされる情報は、全てメッセージで行われる。<br>
しかし、ネットワークのパケットとは異なり、D-Busの各メッセージには、送受信されるデータ1式が含まれていることが保証されている。<br>
<br>
メッセージには、送信されるデータだけでなく、送信者と受信者が誰であるかも記録されており、適切なルーティングを可能にする。<br>
メッセージは、メソッドコール、シグナルエミッション、メソッドの戻り値であり、エラー情報も含むことがある。<br>
<br><br>
 
== 名前空間とアドレス ==
複数のソフトウェアが同一のバス上に存在し、1つのソフトウェアが複数のオブジェクトを提供してメッセージを送信することができるため、<br>
特定の住宅やオフィスを一意に識別するのと同様に、任意のバス上の任意のオブジェクトを効果的かつ一意にアドレス指定する手段を持つことが必要である。<br>
<br>
インターフェース、サービス、オブジェクト名の3つの情報を組み合わせることにより、バス上の任意のオブジェクトに一意のアドレスを作成することができる。<br>
<br>
==== インターフェイス ====
インターフェイスは、バス上に公表される呼び出し可能なメソッドとシグナルのセットである。<br>
<br>
インターフェースは、メッセージを渡すソフトウェアの間で、インターフェースの名前、パラメータ(もしあれば)、戻り値(もしあれば)を定義する"契約"を提供する。<br>
<br>
これらのメソッドは、インターフェイスを使用しているソフトウェアのメソッドやAPIに1対1で直接マッピングされることもある。(直接マッピングされないこともある)<br>
これにより、複数のソフトウェアが、内部の実装に関係なく、類似または同一のインターフェイスを提供することができ、<br>
一方で、ソフトウェアは、ソフトウェアの内部設計を気にすることなく、これらのインターフェイスを使用できるようになる。<br>
<br>
インターフェイスは、文書化やコードの再利用を目的として、XMLで記述する。<br>
ユーザや開発者は、インターフェースのXML記述を参照できるだけでなく、開発者はXMLから自動生成されたクラスを使用することができる。<br>
このため、D-Busの使用は非常に簡単で、エラーが発生しにくくなる。(例. コンパイラがコンパイル時にメッセージの構文を確認することができる)<br>
<br>
==== サービス ====
サービスとは、ソフトウェアとバスの接続を表すものである。<br>
<br>
ここでいうサービスとは、D-Busの用語でいうバス名に相当するものである。<br>
(<u>バス名という用語は、バス上の接続名であり、バスの名前ではない。</u>そのため、Qtのドキュメントで記載されているように、サービスという用語を使用する)<br>
<br>
これらは、複数のコンポーネントの名前空間を必要とする他の多くのシステムで見られるように、"逆ドメイン名"アプローチを使用することにより、一意に保たれる。<br>
KDEプロジェクトのソフトウェアが提供する多くのサービスでは、サービス名にorg.kdeというプレフィックスを使用している。<br>
そのため、セッションバスにおいて、org.kde.screensaverが宣伝されているのを見掛けるかもしれない。<br>
<br>
サービス名には、開発者の組織やソフトウェアのドメイン名を使用することを推奨する。<br>
例えば、開発者のドメインがawesomeapps.org、ソフトウェアの名前がwickedwidgetの場合、バス上のサービス名はorg.awesomeapps.wickedwidgetとなる。<br>
<br>
ソフトウェアが複数のバスに接続している場合、または、同一のソフトウェアの複数のインスタンスが同時にアクティブになっている場合は、接続ごとに一意なサービス名を使用する必要がある。<br>
多くの場合、プロセスIDをサービス名に付加することでこれを実現する。<br>
<br>
==== オブジェクト ====
ソフトウェアは、バス上の複数のオブジェクトへのアクセスを宣伝する可能性が高い。<br>
<br>
オブジェクトとサービスの間のこの多対1の関係は、アドレスにパスコンポーネントを提供することで対応される。<br>
<br>
サービスに関連付けられた各パスは、異なる一意のオブジェクトを表す。(例. /MainInterface、/Documents/Doc1)<br>
実際のパス構造は完全に任意であり、どのようなパスにするかは、サービスを提供するソフトウェア次第である。<br>
これらのパスは、他のソフトウェアにメッセージを送信するソフトウェアのために、オブジェクトを識別し、論理的にグループ化する方法を提供する。<br>
<br>
一部のライブラリは、オブジェクトを適切に名前空間化するために、オブジェクトパスの先頭に"リバースドメイン"を付けてエクスポートする。<br>
これは、任意のサービスに参加するライブラリやプラグインではよくあることで、そのため、ソフトウェアや他のコンポーネントによりエクスポートされたオブジェクトとの全ての衝突を避けなければならない。<br>
しかし、KDEソフトウェアやライブラリでは、この方法は使用されていない。<br>
<br>
オブジェクトはインターフェースへのアクセスを提供しており、オブジェクトは同時に複数のインタフェースへのアクセスを提供することができる。<br>
<br>
==== アドレスの例 ====
D-Busメッセージには、上記で記載したコンポーネントで構成されるアドレスが含まれており、正しいソフトウェア、オブジェクト、メソッドコールにルーティングされる。<br>
# 例. KRunnerのアドレス
org.kde.krunner /App org.kde.krunner.App.display
org.kde.krunner    : サービス
/App                : オブジェクトへのパス
org.kde.krunner.App : オブジェクトがエクスポートするインターフェース
display            : インターフェース内のメソッド
<br>
<br>
もし、/Appオブジェクトがorg.kde.krunner.Appインターフェースを提供するのみの場合(または、実装するサービスの中で表示メソッドが一意の場合)、これはアドレスとして同様に機能する。<br>
D-Busの詳細を知りたい場合は、[[設定 - D-Bus]]のページを参照すること。<br>
org.kde.krunner /App display
<br>
このようにして、可能性のあるそれぞれの宛先を一意かつ確実にアドレスで指定することができる。<br>
<br><br>
 
== メソッドとシグナル ==
バス上の任意のエンドポイントをアドレス指定する方法を使用して、メッセージを送受信する場合の可能性を検討する。<br>
<br>
==== メソッド ====
受信側のソフトウェアにおいて、メソッド(関数)を実行するために送信されるメッセージのことである。<br>
<br>
メソッドの呼び出しに失敗した場合(アドレスの間違い、または、要求されたソフトウェアが動作していない等の理由でメソッドが利用できない場合)、呼び出し側のソフトウェアにエラーが返る。<br>
<br>
メソッドの呼び出しに成功した場合、呼び出し元のソフトウェアにオプションの戻り値が返る。(戻り値が提供されない場合でも、成功メッセージが返る)<br>
このラウンドトリップにはオーバーヘッドがあり、パフォーマンスが重要なコードではこれを念頭に置くことが重要である。<br>
<br>
メソッド呼び出しは、常に呼び出し元のソフトウェアによって開始されて、結果として生じるメッセージは正確に1つの送信元アドレスと1つの送信先アドレスを持つ。<br>
<br>
==== シグナル ====
シグナルはメソッドコールに似ているが、"逆方向"に発生すること、単一の宛先に縛られないことが特徴である。<br>
<br>
シグナルは、インターフェイスをエクスポートしているソフトウェアから発信されて、同一バス上のどのソフトウェアでも利用できる。<br>
これにより、ソフトウェアは、状態の変化やその他のイベントを、それらの変化を追跡するソフトウェアに自発的に知らせることができる。<br>
<br>
これは、Qtのシグナルとスロットの仕組みに似ており、これは、同じ機能のシステム版である。<br>
<br><br>
 
== D-Busコマンド ==
以下の例では、Linuxでのシャットダウン、再起動、ログアウト等のD-Busサービスの一覧を取得している。<br>
dbus-send --print-reply=literal        \
          --system                      \
          --dest=org.freedesktop.login1 \
          /org/freedesktop/login1      \
          --type=method_call            \
          org.freedesktop.DBus.Introspectable.Introspect
<br>
以下の例では、D-Busサービスを使用してシャットダウンおよび再起動を実行している。<br>
# シャットダウン
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)とメソッド名(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)とメソッド名(Reboot)
          boolean:true                              # メソッド名の引数 (引数の型(boolean)と値(true))
<br><br>
 
== D-Bus向け設定ファイル ==
メッセージ・バス・デーモンは、特定のアプリケーションに特化した設定ファイルを持っている。<br>
例えば、ある設定ファイルではメッセージバスをシステム全体のメッセージバスとして設定し、別の設定ファイルではユーザログインセッションごとのバスとして設定することができる。<br>
<br>
また、設定ファイルでは、リソースの制限やセキュリティパラメータ等も設定できる。<br>
<br>
これらの設定ファイルは、相互運用性の仕様の一部ではなく、後方互換性は保証されていないことに注意すること。<br>
<br>
標準的なシステム全体およびセッションごとのメッセージバスの設定は、/usr/share/dbus-1/system.confファイルおよび/usr/share/dbus-1/session.confファイルで設定される。<br>
これらのファイルは、通常、/etc/dbus-1/system-local.confファイルまたはsession-local.confファイルをインクルードしている。<br>
主な設定ファイルを変更しないように、これらのファイルにローカルなオーバーライドすることができる。<br>
<br>
標準のシステムバスは通常、/usr/share/dbus-1/system.dディレクトリから追加のXMLファイルを読み込む。<br>
サードパーティ製のソフトウェアは、dbus 1.10 (2015年にリリース)からサポートされている正しい動作に必要なデフォルトポリシーを、そのディレクトリにインストールする必要がある。<br>
<br>
標準のシステムバスは、通常、/etc/dbus-1/system.dディレクトリからXMLファイルも読み込むため、システム管理者がデフォルトポリシーを上書きしたい場合に使用する。<br>
<br>
<u>ただし、サードパーティ製のソフトウェアは、歴史的に、/etc/dbus-1/system.dディレクトリにXMLファイルをインストールしていたが、現在は非推奨とされており、</u><br>
/etc/dbus-1/system.dディレクトリは、システム管理者のために予約されたものとして扱われるべきである。<br>
<br><br>
 
== 便利なツール ==
D-Busのバスの検索やD-Busを使用したソフトウェアの開発には、いくつかのエンドユーザ向けの便利なツールが存在する。<br>
<br>
==== qdbus ====
<code>qdbus</code>コマンドは、コマンドラインツールであり、与えられたバス上のサービス、オブジェクト、インタフェースを表示したり、バス上の与えられたアドレスにメッセージを送信するために使用できる。<br>
これは、セッションバスおよびシステムバスの両方を検索するために使用することができる。 <br>
<br>
<code>--system</code>オプションを付加する場合は、qdbusはシステムバスに接続する。<br>
<code>--system</code>オプションを付加しない場合は、セッションバスを使用する。<br>
<br>
<code>qdbus</code>コマンドは、与えられた引数を、与えられたオブジェクトに渡すためのアドレス、および、パラメータ(存在する場合)として使用する。<br>
もし、完全なアドレスが与えられなかった場合、バス上のその地点から利用可能な全てのオブジェクトを表示する。<br>
<br>
例えば、アドレスが提供されない場合は、利用可能なサービスのリストが表示される。<br>
サービス名を指定する場合、オブジェクトのパスが提供される。<br>
パスを指定する場合、全てのインターフェースの全てのメソッドが表示される。<br>
<br>
このように、非常に簡単にバス上のオブジェクトを検索して操作することができる。<br>
<br>
==== qdbusviewer ====
<code>qdbusviewer</code>は、Qtで作成されたGUIソフトウェアであり、<code>qdbus</code>コマンドがCUIから提供する機能と基本的に同一のものをGUIとして提供している。<br>
<code>qdbusviewer</code>は、Qt本体と同梱されており、オブジェクトアドレス等のD-Busの基本概念に慣れていれば、誰でも簡単に使用できる。<br>
<br><br>
<br><br>


422行目: 147行目:
<br>
<br>
* QByteArray localMachineId()
* QByteArray localMachineId()
*: D-Busシステムが知っているローカルIDを返します。
*: D-Busシステムが知っているローカルIDを返す。
<br>
<br>
* QDBusConnection sender()
* QDBusConnection sender()
468行目: 193行目:
*: D-Busインターフェースファイルを生成する。
*: D-Busインターフェースファイルを生成する。
<br>
<br>
以下の例では、C++クラスからD-Busインターフェイスファイル (XMLファイル) を生成している。<br>
<br>
まず、インターフェイスを定義したC++クラスを作成する。<br>
<syntaxhighlight lang="c++">
// ExampleObject.h
#include <QObject>
#include <QString>
class ExampleObject : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.example.ExampleInterface")
public slots:
    QString exampleMethod(const QString &input)
    {
        return "Hello, " + input;
    }
};
</syntaxhighlight>
<br>
次に、<code>qdbuscpp2xml</code>コマンドを実行して、D-Busインターフェースファイル (XMLファイル) を生成する。<br>
以下の例では、ExampleObject.hファイルからインターフェイスを読み取り、ExampleInterface.xmlファイルを生成している。<br>
qdbuscpp2xml -m -s ExampleObject.h -o ExampleInterface.xml
<br>
生成されたD-Busインターフェースファイル (XMLファイル) を以下に示す。<br>
<syntaxhighlight lang="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.ExampleInterface">
    <method name="exampleMethod">
      <arg direction="in" type="s" name="input"/>
      <arg direction="out" type="s" name="output"/>
    </method>
  </interface>
</node>
</syntaxhighlight>
<br>
==== qdbusxml2cppコマンド ====
==== qdbusxml2cppコマンド ====
<code>qdbusxml2cpp</code>コマンドは、D-Busインターフェースファイルの定義に従い、C++のアダプターコードを生成する。<br>
<code>qdbusxml2cpp</code>コマンドは、D-Busインターフェースファイルの定義に従い、C++のアダプターコードを生成する。<br>
498行目: 264行目:
* <code>-p <アダプターソースコードのファイル名></code> または <code>-P <アダプターソースコードのファイル名></code>
* <code>-p <アダプターソースコードのファイル名></code> または <code>-P <アダプターソースコードのファイル名></code>
*: アダプターソースコードのファイルへのプロキシコードを生成する。
*: アダプターソースコードのファイルへのプロキシコードを生成する。
<br><br>
== Qtプロジェクト (.pro) / CMakeLists.txt ==
* Qtプロジェクト (.pro) を使用する場合
<syntaxhighlight lang="make">
QT += dbus
</syntaxhighlight>
<br>
<br>
* CMakeLists.txtを使用する場合
<syntaxhighlight lang="cmake">
# QtDBusライブラリの検索 (Qt6を検索して、無ければQt5を使用)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS DBus)
# QtDBusライブラリを検索
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus)
# QtDBusライブラリとのリンク
target_link_libraries(<ターゲット名> PRIVATE
    Qt${QT_VERSION_MAJOR}::DBus
)
</syntaxhighlight>
<br><br>


==== サンプルコード ====
== 使用例 ==
==== pingの送受信 ====
以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。<br>
以下の例では、Qtにおいて、D-Busを使用したメッセージの送受信を行っている。<br>
# セッションバスへの接続を取得する。
# セッションバスへの接続を取得する。
567行目: 355行目:
<br>
<br>
サンプルコードの詳細を知りたい場合は、以下に示すGithubを参照すること。<br>
サンプルコードの詳細を知りたい場合は、以下に示すGithubを参照すること。<br>
https://github.com/PacktPublishing/Hands-On-Embedded-Programming- with-Qt/blob/master/Chapter10/DBusBruteForce/MyObject.cpp<br>
https://github.com/PacktPublishing/Hands-On-Embedded-Programming-with-Qt/blob/master/Chapter10/DBusBruteForce/MyObject.cpp<br>
<br>
==== Firewalldの利用 ====
以下の例では、Firewalldにおいて、以下に示すようなコマンドと同等のものをD-Busを使用して実行している。<br>
sudo firewall-cmd --permanent --zone=<ゾーン名> --add-port=<ポート番号>/tcp
sudo firewall-cmd --reload
<br>
以下に示すサンプルコードを実行するには、適切な権限 (root権限) が必要となることに注意する。<br>
<syntaxhighlight lang="c++">
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
// 開放するポートを指定する。
bool addPort(QDBusInterface &interface, const QString &zone, QString &port, const QString &protocol)
{
    QDBusReply<void> reply = interface.call("addPort", zone, port, protocol, 0);
    return reply.isValid();
}
// 変更を永続化する
bool makePermament(QDBusInterface &interface)
{
    QDBusReply<void> reply = interface.call("runtimeToPermanent");
    return reply.isValid();
}
// Firewalldを再読み込みする
bool reloadFirewall(QDBusInterface &interface)
{
    QDBusReply<void> reply = interface.call("reload");
    return reply.isValid();
}
int main()
{
    // ...略
    // Firewalldの (ポート開放向け) D-Busインターフェースに接続する
    QDBusConnection bus = QDBusConnection::systemBus();
    QDBusInterface interface("org.fedoraproject.FirewallD1",
                            "/org/fedoraproject/FirewallD1",
                            "org.fedoraproject.FirewallD1.zone",
                            bus);
    // 複数のゾーンに対して操作を行うことも可能
    QStringList zones = {"public", "internal", "work"};
    for (const auto& zone : zones) {
      if (addPort(interface, zone, "80", "tcp")) {
          qDebug() << "Port 80 added to" << zone << "zone successfully";
      }
      else {
          qDebug() << "Failed to add port 80 to" << zone << "zone";
          return -1;
      }
    }
    // 変更を恒久的に設定する
    QDBusInterface ifReload("org.fedoraproject.FirewallD1",
                            "/org/fedoraproject/FirewallD1",
                            "org.fedoraproject.FirewallD1",
                            bus);
    if (makePermament(ifReload)) {
      qDebug() << "Changes made permanent";
      return -1;
    }
    if (reloadFirewall(ifReload)) {
      qDebug() << "Firewall reloaded";
      return -1;
    }
    // ...略
    return 0;
}
</syntaxhighlight>
<br>
==== 構造体の送信 ====
以下の例では、D-Busを使用して構造体を送信している。<br>
<syntaxhighlight lang="c++">
// DBusSender.hファイル
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDebug>
struct Hoge {
    QString    str;
    QStringList list;
    int        integer;
    double      fpoint;
};
// D-Bus向けマーシャリング関数
QDBusArgument &operator<<(QDBusArgument &argument, const Hoge &hoge)
{
    argument.beginStructure();
    argument << hoge.str << hoge.list << hoge.integer << hoge.fpoint;
    argument.endStructure();
    return argument;
}
// D-Bus向けマーシャリング関数
const QDBusArgument &operator>>(const QDBusArgument &argument, Hoge &hoge)
{
    argument.beginStructure();
    argument >> hoge.str >> hoge.list >> hoge.integer >> hoge.fpoint;
    argument.endStructure();
    return argument;
}
class DBusSender : public QObject
{
    Q_OBJECT
public:
    DBusSender(QObject *parent = nullptr) : QObject(parent) {}
    void sendStructure(const Hoge &hoge) {
      QDBusMessage message = QDBusMessage::createMethodCall(
            "org.example.hoge",  // D-Busサービス名
            "/org/example/hoge",  // D-Busオブジェクト名
            "org.example.hoge",  // D-Busインターフェース名
            "sendstructure"      // D-Busインターフェースメソッド名
      );
      QVariant variant;
      variant.setValue(hoge);
      message << variant;
      QDBusConnection::sessionBus().send(message);
    }
};
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// main.cppファイル
#include "DBusSender.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 送信する構造体の型をD-Busシステムに登録
    qDBusRegisterMetaType<Hoge>();
    DBusSender sender;
    // 送信データの作成
    Hoge hoge;
    hoge.str    = "Hello, D-Bus!";
    hoge.list    = QStringList{"item1", "item2", "item3"};
    hoge.integer = 100;
    hoge.fpoint  = 3.14f;
    // データを送信
    sender.sendStructure(hoge);
    qDebug() << "Structure sent via D-Bus";
    return a.exec();
}
</syntaxhighlight>
<br>
<code>qDBusRegisterMetaType<構造体>()</code>は、QtのD-Busシステムに対して、カスタム型を登録するための関数呼び出しである。<br>
これは、カスタム型をD-Bus経由で送信する際に必須の操作である。<br>
<br>
* 型の登録
*: この関数は、QtのD-Busシステムに構造体を<u>知らせる</u>ためのものである。
*: D-Busは標準的なデータ型 (整数、文字列等) を扱うことができるが、カスタム型については明示的に登録する必要がある。
*: <br>
* シリアライズとデシリアライズ
*: この登録により、Qtはカスタム型をどのようにシリアライズ (バイトストリームに変換) して、デシリアライズ (バイトストリームから元の型に戻す) すべきかを理解する。
*: これは、D-Busを通じてデータを送受信する時に必要となる。
*: <br>
* マーシャリング関数の使用
*: 登録プロセスは、<code>operator<<</code>と<code>operator>></code>関数を使用する。
*: これらの関数が、カスタム型のマーシャリング (データの変換) 方法を定義している。
*: <br>
* 型の安全性
*: この登録により、D-Bus経由でカスタム型のオブジェクトを安全に送受信できるようになる。
*: Qtは、送信時にこの型をシリアライズして、受信時に正しくデシリアライズすることができる。
*: <br>
* メタオブジェクトシステムとの統合
*: この関数呼び出しは、カスタム型をQtのメタオブジェクトシステムに統合する。
*: これにより、Qtの様々な機能 (シグナル/スロットシステム等) でカスタム型が使用できるようになる。
*: <br>
* 実行時の型情報
*: この登録により、D-Busシステムは実行時にカスタム型の情報を持つことができ、適切なデータ変換を行うことができる。
<br>
したがって、<code>qDBusRegisterMetaType<カスタム型></code>関数を呼び出すことにより、カスタム型をD-Busシステムで使用可能にして、データを送受信できるようにしている。<br>
<br>
 
==== 構造体の受信 ====
<syntaxhighlight lang="c++">
// DBusReceiver.hファイル
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusError>
#include <QDebug>
struct Hoge {
    QString str;
    QStringList list;
    int integer;
    double fpoint;
};
Q_DECLARE_METATYPE(Hoge)
// D-Bus向けマーシャリング関数
QDBusArgument &operator<<(QDBusArgument &argument, const Hoge &hoge) {
    argument.beginStructure();
    argument << hoge.str << hoge.list << hoge.integer << hoge.fpoint;
    argument.endStructure();
    return argument;
}
// D-Bus向けマーシャリング関数
const QDBusArgument &operator>>(const QDBusArgument &argument, Hoge &hoge) {
    argument.beginStructure();
    argument >> hoge.str >> hoge.list >> hoge.integer >> hoge.fpoint;
    argument.endStructure();
    return argument;
}
class DBusReceiver : public QObject {
    Q_OBJECT
    // Q_CLASSINFOマクロを使用して、D-Busインターフェース名を指定
    Q_CLASSINFO("D-Bus Interface", "org.example.hoge")
public:
    DBusReceiver(QObject *parent = nullptr) : QObject(parent) {}
public slots:
    bool sendstructure(const Hoge &hoge)
    {
      try {
          qDebug() << "受信した構造体:";
          qDebug() << "str: " << hoge.str;
          qDebug() << "list: " << hoge.list;
          qDebug() << "integer: " << hoge.integer;
          qDebug() << "fpoint: " << hoge.fpoint;
          return true;
      }
      catch (const std::exception &e) {
          qCritical() << "Error processing received structure: " << e.what();
          return false;
      }
    }
};
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// main.cppファイル
#include "DBusReceiver.h"
bool setupDBusConnection(DBusReceiver &receiver)
{
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.isConnected()) {
        qCritical() << "Cannot connect to the D-Bus session bus:" << connection.lastError().message();
        return false;
    }
    if (!connection.registerService("org.example.hoge")) {
        qCritical() << "Failed to register service:" << connection.lastError().message();
        return false;
    }
    if (!connection.registerObject("/org/example/hoge", &receiver, QDBusConnection::ExportAllSlots)) {
        qCritical() << "Failed to register object:" << connection.lastError().message();
        return false;
    }
    return true;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // カスタム型をD-Busシステムに登録
    qRegisterMetaType<Hoge>("Hoge");
    qDBusRegisterMetaType<Hoge>();
    DBusReceiver receiver;
    if (!setupDBusConnection(receiver)) {
        return -1;
    }
    qDebug() << "D-Bus server is running. Waiting for incoming messages...";
    return a.exec();
}
</syntaxhighlight>
<br>
<code>Q_DECLARE_METATYPE(カスタム型)</code>は、Qtのメタオブジェクトシステムにカスタム型を登録するためのマクロである。<br>
<br>
* 型の登録
*: この宣言により、カスタム型がQtのメタオブジェクトシステムに認識される。
*: これにより、Qtの様々な機能 (シグナル/スロットシステム、プロパティシステム等) でカスタム型を使用できるようになる。
*: <br>
* QVariantとの互換性
*: カスタム型をQVariantオブジェクトに格納、および、QVariant型から取り出すことが可能になる。
*: <br>
* シリアライゼーション
*: Qtの機能を使用してカスタム型のオブジェクトをシリアライズ (バイナリデータに変換)、デシリアライズ (バイナリデータから元のオブジェクトに戻す) することができるようになる。
*: <br>
* 型の安全性
*: コンパイル時の型チェックが可能になり、カスタム型を使用する時の型の安全性が向上する。
*: <br>
* qRegisterMetaTypeとの連携
*: <code>Q_DECLARE_METATYPE</code>と<code>qRegisterMetaType</code>を併用することにより、
*: Qtのスレッド間通信やイベントシステムでもカスタム型を安全に使用できるようになる。
*: <br>
* D-Busとの連携
*: D-Busシステムでカスタム型を使用する場合、この宣言により型情報が適切に処理される。
*: <br>
* 動的プロパティ
*: Qtの動的プロパティシステムでカスタム型を使用することが可能になる。
<br>
具体的な使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
// シグナル / スロットでの使用
signals:
    void hogeChanged(const Hoge &newHoge);
// QVariant型での使用
Hoge myHoge;
QVariant variant = QVariant::fromValue(myHoge);
// D-Busでの使用
QDBusMessage message;
message << QVariant::fromValue(myHoge);
</syntaxhighlight>
<br>
<u>※注意</u><br>
<u><code>Q_DECLARE_METATYPE</code>はヘッダファイル内で使用して、</u><br>
<u>対応する<code>qRegisterMetaType</code>関数呼び出しは、一般的に、<code>main</code>関数内や型を使用する前に1度だけ行う必要がある。</u><br>
<br>
<u>このマクロを使用することにより、カスタム型がQtのシステムにシームレスに統合されて、D-Bus通信を含む様々なQtの機能で安全かつ効率的に使用できるようになる。</u><br>
<br><br>
<br><br>


575行目: 714行目:
  # mocファイルはインクルードしない場合
  # mocファイルはインクルードしない場合
  qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>
  qdbusxml2cpp -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>
  例. qdbusxml2cpp -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.qt.policykit.examples.xml
  例. qdbusxml2cpp -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.dbus.interface.examples.xml
   
   
  # mocファイルもインクルードする場合
  # mocファイルもインクルードする場合
  qdbusxml2cpp -m -a <生成するcppファイル名とヘッダファイル名> -c <自動生成するヘルパークラス名 (親クラス)> -i <対象となるクラスを記述しているヘッダファイル> -l <対象となるクラス名> <D-Busインターフェースファイルのパス>
  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
  例. qdbusxml2cpp -m -a SamplesAdaptor -c SamplesAdaptor -i SampleHelper.h -l SampleHelper org.dbus.interface.examples.xml
<br>
<br>
生成されたファイルをQtプロジェクトに指定およびインクルードする。<br>
生成されたファイルをQtプロジェクトに指定およびインクルードする。<br>
588行目: 727行目:


== CMakeプロジェクト ==
== CMakeプロジェクト ==
CMakeプロジェクトにおいて、<code>qt_add_dbus_adaptor</code>オプションを指定することにより、D-Busインターフェースのアダプタクラスを生成する。<br>
CMakeプロジェクトにおいて、<code>qt_add_dbus_adaptor</code>コマンドを指定することにより、D-Busインターフェースのアダプタクラスを生成することができる。<br>
<code>qt_add_dbus_adaptor</code>コマンドは、Qt D-Bus XMLコンパイラ(qdbusxml2cpp)のアダプタモードでの呼び出しを設定する。<br>
<code>qt_add_dbus_adaptor</code>コマンドは、Qt D-Bus XMLコンパイラ (qdbusxml2cpp) のアダプタモードでの呼び出しを設定する。<br>
<br>
<br>
第2引数で指定したD-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルを生成する。<br>
第2引数で指定したD-Busインタフェースファイル(XML形式)に対するアダプタを実装したC++のソースコードファイルとヘッダファイルを生成する。<br>
602行目: 741行目:
<br>
<br>
第4引数から第6引数までを省略する場合、親クラス名、ヘッダファイル名、クラス名は、第3引数の指定値から自動的に生成される。<br>
第4引数から第6引数までを省略する場合、親クラス名、ヘッダファイル名、クラス名は、第3引数の指定値から自動的に生成される。<br>
<syntaxhighlight lang="cmake">
<syntaxhighlight lang="cmake">
  qt_add_dbus_adaptor(
  qt_add_dbus_adaptor(
   <任意の変数名>
   <任意の変数名>             # 生成されるソースファイル名を指定  例: SampleAdaptor.cpp  SampleAdaptor.h
   <D-Busインタフェースファイル(XML形式)>
   <D-Busインタフェースファイル>  # XML形式
   <親クラスのヘッダファイル名>
   <親クラスのヘッダファイル名>
   [第3引数のクラス名 (省略可能)]
   <第3引数のクラス名>        # 省略可能
   [生成するヘッダファイル名(拡張子.hは不要) (省略可能)]
   <生成するヘッダファイル名>    # 省略可能
   [生成するアダプタのクラス名 (省略可能)]
                            # 拡張子.hは不要
   <生成するアダプタのクラス名>  # 省略可能
  )
  )
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]