使用 QDBusInterface 获取 DBus 接口属性时出错

Posted

技术标签:

【中文标题】使用 QDBusInterface 获取 DBus 接口属性时出错【英文标题】:Error getting DBus Interface property with QDBusInterface 【发布时间】:2013-12-01 07:41:22 【问题描述】:

我正在尝试使用 NetworkManager DBus 接口为我在 Qt 中的所有接口获取网络信息(IP 地址、网络掩码、路由等)。问题是当我尝试访问 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 获得上述接口提到的属性的良好值。以下是我的代码 sn-p。

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, prefix, gateway), (addr, prefix, gateway), .. .]"

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

想法/建议

问候, 法鲁克·阿尔沙德。

【问题讨论】:

【参考方案1】:

就我的研究而言,问题与类型转换有关。属性值采用 aau 的形式(根据 NM Dbus 文档)。 QDbusInterface.property 返回 QVariant。它确实找到了该属性,但无法确定该属性的类型,因此给了我错误消息。但我担心的是,我已经在 Qt 元对象系统中注册了此属性的自定义类型,正如我在更新 #1 中提到的那样,为什么它给我这个错误我的类型已正确地向系统注册并且 qDBusRegisterMetaType 确实返回了我一个有效的整数。在 Qt 5.1 中,错误的根源在于 qdbusmaster.cpp。一篇文章建议注册元类型,如下所述,但无济于事。

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

目前我没有时间进一步深入研究是否是一些错误或我遗漏了什么,但一旦我有实际的解决方案,我会更新这篇文章。

解决方法

以下解决方法可以读取给定的属性值。要使此解决方法起作用,您需要添加 qdbus-private 而不是 qdbus 和 include 。

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

【讨论】:

【参考方案2】:

我为此找到的最佳解决方案是编写 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]));

【讨论】:

以上是关于使用 QDBusInterface 获取 DBus 接口属性时出错的主要内容,如果未能解决你的问题,请参考以下文章

使用 dbus-send 设置/获取属性

您如何真正使用 dbus 从 NetworkManager 获取可见 SSID 列表?

使用pidgin dbus python api获取xmpp资源字符串

如何使用 dbus/bluez 在 python 中获取当前 MTU

从 DBUS org.freedesktop.dbus 和 java 获取数据 - org.freedesktop.DBus$Error$UnknownMethod: 方法不存在

QDBus 问题:获取 org.freedesktop.DBus.Error.UnknownMethod,但方法存在