Ошибка при получении свойства интерфейса DBus с QDBusInterface

Я пытаюсь получить информацию о сети (IP-адрес, маска сети, маршрут и т. Д.) Для всех моих интерфейсов в Qt, используя интерфейс DBus NetworkManager. Проблема в том, что когда я пытаюсь получить доступ к свойству "Адреса" org.freedesktop.NetworkManager.IP4Config, я получаю следующую ошибку

QDBusAbstractInterface: type QDBusRawType<0x616175>* must be registered with QtDBus before it can be used to read property org.freedesktop.NetworkManager.IP4Config.Addresses
Addresses are invalid 
Error 2 =  "Unregistered type QDBusRawType<0x616175>* cannot be handled"

Однако я могу получить значение этого свойства с помощью dbus-send с помощью следующей команды.

dbus-send --system --print-reply --dest=org.freedesktop.NetworkManager \
     /org/freedesktop/NetworkManager/IP4Config/0 \
     org.freedesktop.DBus.Properties.Get \
     string:"org.freedesktop.NetworkManager.IP4Config" \
     string:"Addresses" 

Я также могу получить хорошие значения для упомянутого выше свойства интерфейса через qtdbusviewer. Ниже приведен мой фрагмент кода.

QDBusInterface interface(NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_IFACE, QDBusConnection::systemBus());

// Get a list of all devices
QDBusReply<QList<QDBusObjectPath> > result = interface.call("GetDevices");
foreach (const QDBusObjectPath& connection, result.value()) {
    QDBusInterface device(NM_DBUS_SERVICE, connection.path(), "org.freedesktop.NetworkManager.Device", QDBusConnection::systemBus());
    if ( device.property("DeviceType").toInt() == NM_DEVICE_TYPE_ETHERNET ) {

        // Get the IPv4 information if the device is active
        if ( device.property("State").toInt() == NETWORK_DEVICE_CONNECTED ) {
            QVariant ipv4config = device.property("Ip4Config");
            if ( ipv4config.isValid() ) {
                QDBusObjectPath path = qvariant_cast<QDBusObjectPath>(ipv4config);
                QDBusInterface ifc(NM_DBUS_SERVICE, path.path(), "org.freedesktop.NetworkManager.IP4Config", QDBusConnection::systemBus());
                if ( ifc.isValid() ) {
                    qDebug() << "Error 1 = " << ifc.lastError().message(); // No error. Everything is OK.
                    QVariant addresses = ifc.property("Addresses");     // Throwing the QDBusAbstractInterface Error where the property is good and does exist.
                    if ( addresses.isValid() ) {
                        qDebug () << "Addresses are valid";
                    } else {
                        qDebug () << "Addresses are invalid";
                    }
                    qDebug() << "Error 2 = " << ifc.lastError().message();
                }
            }
        }
    }
}

ОБНОВЛЕНИЕ № 1

Я думаю, что это проблема типов. Система типов Qt-Dbus не понимает тип свойства "Адреса", поэтому не может создать из него QVariant. Поэтому я добавил следующие строки перед чтением свойства "Адреса". NETworkManager определяет свойство Addresses как следующий тип, поэтому я думаю, что мой typedef хорош.

aau - "Массив кортежей IPv4-адреса / префикса / шлюза. Все 3 элемента каждого кортежа расположены в сетевом порядке байтов. По существу: [(addr, префикс, шлюз), (addr, префикс, шлюз), ...]"

typedef QList<QList<uint> > Addresses;
Q_DECLARE_METATYPE(Addresses)


qDBusRegisterMetaType<Addresses>()
QVariant addresses = ifc.property("Addresses");

Также я перешел на Qt 5.1 (ранее я использовал 4.8), и я получаю ту же ошибку в следующей форме.

Cannot construct placeholder type QDBusRawType

Мысли / Предложения

С уважением, Фаррух Аршад.

2 ответа

Что касается моих исследований, то проблема связана с преобразованием типов. Значение свойства в форме aau (согласно документации NM Dbus). QDbusInterface.property возвращает QVariant. Он находит свойство, но не может определить тип свойства, поэтому выдает сообщение об ошибке. Но меня беспокоит то, что я зарегистрировал пользовательский тип этого свойства в системе мета-объектов Qt, как я уже упоминал в обновлении № 1, тогда почему он дает мне эту ошибку, мой тип был зарегистрирован в системе должным образом, и qDBusRegisterMetaType действительно вернул меня действительное целое число. В Qt 5.1 источник ошибки находится в qdbusmaster.cpp. Одна статья предлагает зарегистрировать метатип, как упомянуто ниже, но безрезультатно.

qRegisterMetaType<Addresses>("Addresses");
qDBusRegisterMetaType<Addresses>();

На данный момент у меня нет времени для дальнейшего изучения, если это какая-то ошибка или я что-то упустил, но я обновлю этот пост, как только у меня будет реальное решение.

Временное решение

Следующий обходной путь будет работать, чтобы прочитать данное значение свойства. Чтобы этот обходной путь работал, вам нужно добавить qdbus-private вместо qdbus и включить.

QVariant ipv4config = device.property("Ip4Config");
if ( ipv4config.isValid() ) {
    QDBusObjectPath path = qvariant_cast<QDBusObjectPath>(ipv4config);

    QDBusMessage message = QDBusMessage::createMethodCall(NM_DBUS_SERVICE, path.path(),       QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
    QList<QVariant> arguments;
    arguments << "org.freedesktop.NetworkManager.IP4Config" << "Addresses";
    message.setArguments(arguments);
    QDBusConnection connection = QDBusConnection::systemBus();
    QDBusMessage reply = connection.call(message);

    foreach(QVariant var, reply.arguments()) {
        qDebug () << "String = " << QDBusUtil::argumentToString(var).toHtmlEscaped();
    }
}

Строка покажет вам IP-адрес / маску подсети / IP-адрес маршрутизатора, которые вы должны будете извлечь из вывода. Для справки, я взял этот подход от qdbusviewer.

Это не правильное решение, но оно пока избавит вас от неприятностей. Также есть хорошая статья, предлагающая использование пользовательских типов с Qt Dbus. http://techbase.kde.org/Development/Tutorials/D-Bus/CustomTypes

Лучшее решение, которое я нашел для этого, было написать реализацию QDBusAbstractInterface:

typedef QList<QList<uint> > UIntListList;
Q_DECLARE_METATYPE(UIntListList)

class DBusIP4ConfigInterface : public QDBusAbstractInterface
{
    Q_OBJECT

public:
    DBusIP4ConfigInterface(const QString &service, const QString &path, const QDBusConnection &connection,
                    QObject *parent = 0)
    {
        qDBusRegisterMetaType<UIntListList>();
    }
    virtual ~DBusIP4ConfigInterface() { }

    Q_PROPERTY(UIntListList Addresses READ addresses)
    UIntListList addresses() const
    {
            return qvariant_cast<UIntListList>(property("Addresses"));
    }

    Q_PROPERTY(QString Gateway READ gateway)
    QString gateway() const
    {
            return qvariant_cast<QString>(property("Gateway"));
    }

Q_SIGNALS:
    void PropertiesChanged(const QVariantMap &properties);
};

Это имеет дополнительное преимущество в простоте использования:

UIntListList addresses = m_dbusIP4Config->addresses();
Q_ASSERT(addresses.size() >= 1);
Q_ASSERT(addresses[0].size() == 3);

QHostAddress ip = QHostAddress(qFromBigEndian(addresses[0][0]));
Другие вопросы по тегам