Search code examples
c++qtdbus

Identify original call from QDBusPendingCallWatcher in catch-all callback


I want to use Qt's QDBusPendingCallWatcher to keep track of the response of some D-Bus calls. There are several different D-Bus calls I can make, but I want to have a single callback for all of them.

The problem is that the finished signal that QDBusPendingCallWatcher emits when the return value is ready only has a pointer to QDBusPendingCallWatcher itself, and I have found no way of getting the original QDBusMessage (which contains the method that was called).

Digging deep in to Qt's source code you can see that calling asyncCall creates the QDBusMessage which is in turn passed to the QDBusConnection being used, but sadly the info is stored using the pimpl pattern in QDBusPendingCallPrivate and seems to be impossible to recover from client code.

TL;DR: Would it be possible to know the method name of an async D-Bus call from within the slot fired by the QDBusPendingCallWatcher::finished signal?


Solution

  • As you have found out, it seems that there's no way to get the original message back. However, there's at least two work-arounds that I can see. Since QDbusPendingCallWatcher inherits from QObject, you can set a property on the watcher that keeps the name of the method call around. Or, you could have a lambda function that adds in more information to the callback(e.g. the method name).

    DBusCaller::DBusCaller(QObject *parent) : QObject(parent)
    {
        QDBusInterface connSettings("org.freedesktop.NetworkManager",                                    "/org/freedesktop/NetworkManager/Settings/1",                              
    "org.freedesktop.NetworkManager.Settings.Connection",
                                QDBusConnection::systemBus() );
    
        //Method 1: set a property on the object
        {
            QDBusPendingCall pending = connSettings.asyncCall( "GetSettings" );
            QDBusPendingCallWatcher* watch = new QDBusPendingCallWatcher( pending );
            watch->setProperty( "method", "GetSettings" );
            connect( watch, &QDBusPendingCallWatcher::finished,
                 this, &DBusCaller::callFinished );
        }
    
        //Method 2: use a lambda to add more information to our callback
        {
            QString methodName = "GetSettings";
            QDBusPendingCall pending = connSettings.asyncCall( methodName );
            QDBusPendingCallWatcher* watch = new QDBusPendingCallWatcher( pending );
            connect( watch, &QDBusPendingCallWatcher::finished,
                 this, [=](QDBusPendingCallWatcher* reply) { callFinishedString( methodName, reply ); });
        }
    }
    
    void DBusCaller::callFinished(QDBusPendingCallWatcher* pending){
        pending->deleteLater();
        qDebug() << "Pending finished.  Method: " << pending->property( "method" );
    }
    
    void DBusCaller::callFinishedString(QString methodName, 
    QDBusPendingCallWatcher *watch){
        watch->deleteLater();
        qDebug() << "Pending finsihed.  Method: " << methodName;
    }