如何在Qt DBus调用中从QDBusMessage中提取返回的数据

How do I extract the returned data from QDBusMessage in a Qt DBus call?

本文关键字:提取 QDBusMessage 返回 数据 Qt DBus 调用      更新时间:2023-10-16

我正在尝试使用Qt的QDBus调用WPA请求方的DBus接口类库。特别是,我正在尝试使用"Get"属性调用以检索"接口"属性值。

"Get"的DBus规范(通过内省(是:

<interface name="org.freedesktop.DBus.Properties">
    <method name="Get">
        <arg name="interface" type="s" direction="in"/>
        <arg name="propname" type="s" direction="in"/>
        <arg name="value" type="v" direction="out"/>
    </method>
    ...
</interface>

似乎很简单。两个字符串输入,输出为变体(这些是 DBus 类型(。对于"接口"属性,我是期望变体是对象路径数组(DBus 类型"ao"(。

我正在使用QDBusInterface::call()来调用 DBus 方法,该方法返回一个QDBusMessage,但我无法弄清楚如何提取我的数据由此。

QDBusMessage::arguments()返回一个QList<QVariant>。我试过了此列表中的项目的各种转换,以尝试找到我的对象路径数组,但我似乎最终得到一个空字符串相反。

QVariant::type()似乎应该有所帮助,但似乎只是返回类型 QDBusMessage ,这显然是错误的。例如:

// 'message' is of type QDBusMessage
qDebug() << "Argument 0 type is" << message.arguments().at(0).type();

指纹:

Argument 0 type is QVariant::QDBusMessage

如何提取实际的消息数据?

我发现最简单的方法是使用qDebug()随时打印结果。这通常会指出下一步需要转换为哪种类型,直到最终到达最里面的类型。

Qdbusviewer 是一个有用的工具,用于确定 DBus 参数将是必需的。在这种情况下:

  • WPAS服务:">fi.w1.wpa_supplicant1">
  • WPAS 路径:"/fi/w1/wpa_supplicant1">
  • 属性接口标识符:">org.freedesktop.DBus.Properties">
  • WPAS 接口标识符:"fi.w1.wpa_supplicant1">

在初始化调用GetQDBusInterface时,我们需要使用 Properties接口,因为这是提供Get的接口方法。

在使用QDBusInterface::call()方法调用Get时,第二个和第三个参数对应于内省输出("interface""propname"(。 "interface"是在哪里可以找到该属性,该属性用于"Interfaces"属性是"fi.w1.wpa_supplicant1"(这可以使用qdbusviewer确认(。

"propname"参数只是属性的名称: 在这种情况下"Interfaces"

到目前为止的代码:

std::string getInterface()
{
    QDBusInterface interface( "fi.w1.wpa_supplicant1",
                              "/fi/w1/wpa_supplicant1",
                              "org.freedesktop.DBus.Properties",
                              QDBusConnection::systemBus() );
    // Calls DBus method
    QDBusMessage result = interface.call( "Get",
                                          "fi.w1.wpa_supplicant1",
                                          "Interfaces" );

这是困难的部分。 QDBusInterface::call()返回一个QDBusMessage,其中有我们的财产信息被困在里面。

    qDebug() << result;

此调试语句打印:

QDBusMessage(type=MethodReturn, service=":1.2431", signature="v", contents=([Variant: [ObjectPath: /fi/w1/wpa_supplicant1/Interfaces/7/Networks/0]]) )

看起来不错。"ObjectPath"是我们追求的,它肯定在那里的某个地方。

接下来我们需要QDBusMessage::arguments(),它"返回列表将从 D-Bus 发送或接收的参数。它返回一个QList<QVariant>

    QList<QVariant> outArgs = result.arguments();
    qDebug() << outArgs;

调试语句打印:

(QVariant(QDBusVariant, ) )

这个"符号"有点不清楚(括号是指列表吗?(,但我们会继续前进。

    QVariant first = outArgs.at(0);
    qDebug() << first;

指纹:

QVariant(QDBusVariant, )

所以外括号似乎确实表示一个数组,尽管为什么有是内集中使用的逗号,而不是外集的逗号是有点一个谜。

当我们遇到类型时,我们会不断转换它们:

    QDBusVariant dbvFirst = first.value<QDBusVariant>();
    //qDebug() << dbvFirst; // compile error!

qDebug()不懂QDBusVariant,所以没有调试打印可在此处获得。相反,如果我们查看文档 QDBusVariant,我们看到它提供了一种variant()的方法转换为常规QVariant类型。

    QVariant vFirst = dbvFirst.variant();
    qDebug() << vFirst;

我们似乎确实在兜圈子,但打印输出有点这次不同:

QVariant(QDBusArgument, )

另一个转换:

    QDBusArgument dbusArgs = vFirst.value<QDBusArgument>();

不幸的是,qDebug()在这里也不起作用。The QDBusArgument类型可以容纳许多不同的元素类型,这些类型被描述在 Qt 文档中。 QDBusArgument::currentType()告诉你哪个您拥有的类型。在我们的例子中:

    qDebug() << "QDBusArgument current type is" << dbusArgs.currentType();

指纹:

QDBusArgument current type is 2

2 表示 ArrayType

根据QDBusArgument文档,我们可以提取元素,使用如下所示的代码:

    QDBusObjectPath path;
    dbusArgs.beginArray();
    while (!dbusArgs.atEnd())
    {
        dbusArgs >> path;
        // append path to a vector here if you want to keep it
    }
    dbusArgs.endArray();

我假设数组元素类型是 QDBusObjectPath ,因为此时它使感觉是这样。如果我是对的,那就很清楚了。

如果收到错误消息QDBusArgument: write from a read-only object,请将dbusArgs的声明更改为:

    const QDBusArgument &dbusArgs = vFirst.value<QDBusArgument>();

qDebug()也不支持QDBusObjectPath,但是 QDBusObjectPath::path()返回一个QString,因此我们可以进行调试像这样打印:

    qDebug() << path.path();

指纹:

"/fi/w1/wpa_supplicant1/Interfaces/7"

最后!

我的目标是获取GetInterface fi.w1.wpa_supplicant1接口方法返回的对象路径。

@MatthewD的回答对我开始实验真的很有帮助,但不幸的是,它并没有按照要求对我有用。我尝试了所有的可能性。但最后,不知何故,我以不同且更短的方式获得了所需的结果。

我所做的是:
- 我有界面:


QDBusInterface interface("fi.w1.wpa_supplicant1", "/fi/w1/wpa_supplicant1", "fi.w1.wpa_supplicant1", QDBusConnection::systemBus());- 调用方法并存储消息


QDBusMessage mesg = interface.call("GetInterface", "wlan0");- 然后获取第一个参数


QVariant var = mesg.arguments().at(0);- 然后获取对象路径


QDBusObjectPath objpath = var.value<QDBusObjectPath>();- 最后


QString path_str = objpath.path();

现在将路径打印为字符串:
printf("Object path: %sn", path_str);

好吧,经过这么长时间!

我认为在调试回复并看到其QDBusVariant之后......

结果的variant()方法QDBusVariant返回QDBus变体作为QVariant对象..因此,调用:

const auto &resultArg = result.arguments().at(0).value<QDBusVariant>().variant();

返回一个QVariant..,我们可以轻松地在调试中打印或转换为对象中的存储值。