QtDbus-謎に包まれた闇。 パート1

私たちの旅はQt Graphics Frameworkから始まり、その明るい側面に採用されてから、長い間、体のさまざまな部分で熊手を獲得しました。



この記事はメインプロットから派生したものです。 その中で、物語はQtDBusについてのものになります。 このQtモジュールは4番目のバージョンに登場し、何らかの形で文書化され、例を提供しました。 しかし、Qt 5.0が攻撃され、その理由はわかりませんが、上記のドックが暗闇の側にあるという事実に至りました。 。

ケース番号1。 作り方と作り方


QtドックでDBusを操作するロジックを理解しようとすることは、ありがたい仕事です。 もっと良いものがあります-これはdbus開発者自身によるチュートリアルです。 そして、新しいことは何も言いませんが、記事の完全性のために、このすべてがどのように機能するかの一般的な概要を説明します。

コンセプト自体:

画像 バスで送信される各D-Busメッセージには、独自の送信者がいます。 メッセージがブロードキャスト信号ではない場合、受信者がいます。 送信者と受信者のアドレスはオブジェクトパスと呼ばれます。これは、D-Busが各アプリケーションがオブジェクトのセットで構成され、メッセージがアプリケーション間ではなく同じアプリケーションのオブジェクト間で転送されると想定するためです。



したがって、オブジェクトへのアクセスを開くには、最も単純な場合には、次のものが必要です。

  1. バス上のデーモンに接続します。 これには、 QDBusConnectionを使用する必要があります。標準バスには静的メソッドがあることに注意してください。
  2. そこにあなたの名前を登録してください。 これは、通常の、読み取り可能な、そして最も重要なのは、彼らが私たちに接続するための固定名を持ちたい場合です。これには、 QDBusConnection :: registerService()メソッドがあります。
  3. そして、何らかの方法でオブジェクトを登録します( QDBusConnection :: registerObject() )。
  4. インターフェースを選択します。まあ、彼は名前を設定する必要もあります。


使用はそれほど複雑ではないようです。 デバッグの問題が残っています。



このために、プログラムと方法の全範囲があります。



ケース番号2。 接続を確立しようとしています。

準備を整えて、地獄の門を開いてください。 この例に基づいて最初に行うことは、接続の確立です。 相互作用する2つのプロジェクト、PingとPong( この例に類似)を作成しましょう。

Pingプロジェクト:

main.cpp
#include <stdio.h> #include <QObject> #include <QCoreApplication> #include <QDBusConnection> #include <QDBusConnectionInterface> #include <QDBusServiceWatcher> #include <QDebug> #include "Ping.h" #include "../serviceNameAndProperty.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); if (!QDBusConnection::sessionBus().isConnected()) { fprintf(stderr, "Cannot connect to the D-Bus session bus.\n" "To start it, run:\n" "\teval `dbus-launch --auto-syntax`\n"); return 1; } qDebug()<<"Ping connected to D-bus"; Ping ping; QDBusConnectionInterface *iface = QDBusConnection::sessionBus().interface(); QObject::connect(iface, SIGNAL(serviceRegistered(QString)), &ping, SLOT(connectToService(QString))); QObject::connect(iface, SIGNAL(serviceUnregistered(QString)), &ping, SLOT(disconnect(QString))); QStringList registedServices = iface->registeredServiceNames(); if(registedServices.contains(ping.m_aviableServiceName)) ping.connectToService(ping.m_aviableServiceName); return a.exec(); }
      
      



Ping.h
 #ifndef PING_H #define PING_H #include <QObject> #include <QDBusAbstractInterface> #include <qdbusinterface.h> class Ping : public QObject { Q_OBJECT public: explicit Ping(QObject *parent = 0); public slots: void connectToService(const QString &name); void disconnect(const QString &name); public: QString m_aviableServiceName; private: QDBusInterface *m_interface; QString m_interfaceName; static const QString _propertyName; }; #endif // PING_H
      
      



Ping.cpp
 #include "Ping.h" #include "../serviceNameAndProperty.h" #include <QDBusConnectionInterface> #include <QDebug> const QString Ping::_propertyName(QUIOTING(IMAGE_DATA_SHARED_ID)); Ping::Ping(QObject *parent) : QObject(parent) { m_interface = NULL; m_interfaceName = QString(BUFFER_NAME); m_aviableServiceName = QString(SERVICE_NAME); } void Ping::connectToService(const QString &name) { if(name != m_aviableServiceName) return; qDebug()<<"Connceting"; m_interface = new QDBusInterface(name, "/", m_interfaceName, QDBusConnection::sessionBus(), this); if(!m_interface->isValid()){ qDebug()<<"Invalid interface"<<m_interface->lastError(); delete m_interface; m_interface = NULL; return; } qDebug()<<m_interface->interface(); QVariant var("ku"); var = m_interface->property("imageDataSharedId"); qDebug()<<var; } void Ping::disconnect(const QString &name) { if(name != m_aviableServiceName) return; if(name != m_interface->service()) return; delete m_interface; m_interface = NULL; qDebug()<<"Disconnect"; }
      
      





Pongプロジェクト:

main.cpp
 #include <QCoreApplication> #include <QDBusConnection> #include <QDBusError> #include <QDebug> #include "Pong.h" #include "../serviceNameAndProperty.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QObject obj; Pong *pong = new Pong(&obj); if( ! QDBusConnection::sessionBus().registerObject("/", &obj)){ fprintf(stderr, "%s\n", qPrintable("Can't register object")); exit(1); } qDebug()<<"Pong connected to D-bus"; if (!QDBusConnection::sessionBus().registerService(SERVICE_NAME)) { fprintf(stderr, "%s\n", qPrintable(QDBusConnection::sessionBus().lastError().message())); exit(1); } qDebug()<<"Test service start"; return a.exec(); }
      
      



Pong.h
 #ifndef PONG_H #define PONG_H #include <QDBusAbstractAdaptor> #include <QDBusVariant> #include "../serviceNameAndProperty.h" class Pong : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", BUFFER_NAME) Q_PROPERTY(QString IMAGE_DATA_SHARED_ID READ imageDataSharedId) public: explicit Pong(QObject *parent = nullptr); QString imageDataSharedId(); private: QString m_imageDataSharedId; }; #endif // PONG_H
      
      



Pong.cpp
 #include "Pong.h" Pong::Pong(QObject *parent) : QDBusAbstractAdaptor(parent) { m_imageDataSharedId = "testImageBufferId"; } QString Pong::imageDataSharedId() { return m_imageDataSharedId; }
      
      





共有ファイルserviceNameAndProperty.h
 #ifndef SERVICENAMEANDPROPERTY_H #define SERVICENAMEANDPROPERTY_H #define SERVICE_NAME "ru.sonarh.dbus.pong" #define BUFFER_NAME "buffer" #define IMAGE_DATA_SHARED_ID imageDataSharedId #define QUIOTING(text) #text #endif // SERVICENAMEANDPROPERTY_H
      
      





プロジェクトを収集し、最初にpingを開始し、次にピンポンを開始します。 しかし、結果は予想外です。



言い換えると、pingはピンポンの外観を認識していません。 混乱して、qdbusviewerコードに目を向けます。

 QDBusConnectionInterface *iface = c.interface(); connect(iface, SIGNAL(serviceRegistered(QString)), this, SLOT(serviceRegistered(QString))); connect(iface, SIGNAL(serviceUnregistered(QString)), this, SLOT(serviceUnregistered(QString))); connect(iface, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(serviceOwnerChanged(QString,QString,QString)));
      
      





同じようです。 しかし、いいえ、それらは完全に異なるスロットを持っています:

 void QDBusViewer::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { QModelIndex hit = findItem(servicesModel, name); if (!hit.isValid() && oldOwner.isEmpty() && !newOwner.isEmpty()) serviceRegistered(name); else if (hit.isValid() && !oldOwner.isEmpty() && newOwner.isEmpty()) servicesModel->removeRows(hit.row(), 1); else if (hit.isValid() && !oldOwner.isEmpty() && !newOwner.isEmpty()) { servicesModel->removeRows(hit.row(), 1); serviceRegistered(name); } }
      
      





突然、コードはドックからの例のように見えます。 さて、私たちは自宅で似たようなものを書いています:

 void Ping::manageConnection(const QString& name, const QString &oldVAlue, const QString &newValue) { if(name != m_aviableServiceName) return; if(newValue.isEmpty()) disconnect(name); else connectToService(name); }
      
      





そしてserviceOwnerChangedのみが機能することを確認してください 。 しかし、それだけではありません。トロールは、このシグナルは非推奨であると警告しています。 では、次のコードを書きます。

 QDBusServiceWatcher watcher; watcher.setConnection(QDBusConnection::sessionBus());; QObject::connect(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),&ping, SLOT(manageConnection(QString,QString,QString)));
      
      



コンパイルして実行します。 動作しません...ねえ、トロル、太りすぎ! このことの使い方を教えてください。 いいえ、行を追加するとわかります

  watcher.addWatchedService(ping.m_aviableServiceName);
      
      



すべてが機能し、サービスの登録と登録解除のシグナルを受信し始めることもありますが、正確な名前は知らず、マスクだけがわからない場合はどうでしょうか?



ケース番号3。 試みられた仕事。
だから、我々は最初のラウンドを克服しました。 しかし、彼の直後に2番目が来ます! そして、それはこのように見えます:



つまり インターフェイスを作成できません。 qdbusviewerに再び登ると、そこに次の行が表示されます。

  QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get")); QList<QVariant> arguments; arguments << sig.mInterface << sig.mName; message.setArguments(arguments); c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage)));
      
      





興味深いオプション、はい、それは動作します。 しかし、ドックは私たちにもっと柔らかく、より抽象的な何かを約束します。 ここでこれらの行を

  QDBusInterface iface(sig.mService, sig.mPath, sig.mInterface,c); if( !iface.isValid()) qDebug()<<(QDBusError(iface.lastError()).message()); else qDebug()<<iface.property(sig.mName.toLatin1().data());
      
      



私たちを止めた問題は再び起こります。

したがって、問題はありますが、その理由は明確ではありません。 最初の欲求は、Qtのソースコードを取得することです。 解決策は前線ですが、1時間で成功しませんでした。私の脳はかなり緊張しました。 解決策は側面から来ました。KDEでインターフェースがどのように構築されるかを見て、

命名形式とその目的を覚えやすくするために、次の表を使用できます。



サービス名 ネットワークのホスト名 ドット区切り(「ホスト名のように見える」)
オブジェクトパス URLパスコンポーネント スラッシュ区切り(「パスのように見える」)
インターフェース プラグイン識別子 ドット区切り
これは推奨事項や関連付けではなく、拘束力のある合意です。 実際、 BUFFER_NAMEをあいまいなfdgfsgf.bufferに置き換えると、すべてが機能します。

D-Busをより注意深く調べると、インターフェース名にドットが必要であることがわかりますが、なぜqdbusviewerで提案されたオプションが機能するのですか?



ボーナスレベル

pongの場合、main.cppで、次のように最初の行を作成します。

 Pong pong; if( ! QDBusConnection::sessionBus().registerObject("/", &pong)){
      
      



それから私のプログラムはセグメンテーション違反で落ちます。



おわりに


当初、この記事は単一の記事として計画されていましたが、質問を検討するにつれて、ますます多くの質問が蓄積され、記事は成長し、成長しました。 そして、基本を学んだ後に初めて現在のサイズに成長しました。次に何が起こるでしょうか?



参照資料



All Articles