qtdbusqdbus

Using QDBusServer under Windows


I have been playing around with QDBusServer lately, but can't quite get it to work and the lack of proper documentation/examples is staggering.

This is my test code.

#include <QCoreApplication>
#include <QDBusInterface>
#include <QDBusReply>
#include <QTimer>
#include <QFile>

#ifndef PERFORM_RPC
#include <QDBusServer>
#include "DBusService.h"
#endif

static QString readAddress(QFile& file)
{
    QString rc;
    if (file.open(QFile::ReadOnly))
    {
        rc = file.readAll();
        file.close();
    }
    return rc;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QFile confFile(qApp->applicationDirPath() + "/dbus.conf");
#ifndef PERFORM_RPC
    QDBusServer server;
    if (!server.isConnected())
    {
        fprintf(stderr, "D-Bus server not connected: %s\n", qPrintable(server.lastError().message()));
        return 1;
    }
    if (confFile.open(QFile::WriteOnly))
    {
        confFile.write(server.address().toUtf8());
        confFile.close();
    }
#endif
    auto bus = QDBusConnection::connectToBus(readAddress(confFile), qApp->applicationName());

    if (!bus.isConnected()) {
        qCritical() << "D-Bus not connected:" << bus.lastError().message();
        return 1;
    }

#ifdef PERFORM_RPC
    QTimer::singleShot(0, &a, [&]()
    {
        QDBusInterface iface("com.example.TestInterface",
                             "/test/instance",
                             "com.example.TestInterface",
                             bus);
        QDBusReply<QString> reply;

        if (!iface.isValid())
            fprintf(stderr, "D-Bus Interface ungültig: %s\n", qPrintable(bus.lastError().message()));
        else if ((reply = iface.call("sayHello", "Refugnic")).isValid())
            printf("RPC response: %s\n", qPrintable(reply.value()));
        else
            fprintf(stderr, "RPC call has failed\n");
        a.quit();
    });
#else
    DBusService service;

    if (!bus.registerService("com.example.TestInterface")
        || !bus.registerObject("/test/instance", &service, QDBusConnection::ExportAllSlots)) {
        fprintf(stderr, "Registration failed: %s\n", qPrintable(bus.lastError().message()));
        return 1;
    }
#endif
    return a.exec();
}

This code is built twice, once for the server application, once for the client application.

What happens:

According to 'you know whom' the issue sits with 'connectToBus' attempting to establish a connection with the tcp port in a blocking manner, whilst the server process cannot accept said connection, because the process is...well, trying to connect to the server.

Attempting to run the connect in a separate thread has lead to all sorts of 'fun' memory errors, apparently because DBus connections are required to be run on the main thread.

Has anyone ever worked successfully with DBusServer under Windows and could give me a pointer or two?

Thank you.


Solution

  • because the process is...well, trying to connect to the server.

    Don't make it connect to the server. The server should not be connecting back to itself. It already has a connection to itself by nature of being the server.

    But even if it could, that connection wouldn't lead it to any shared 'bus' where clients can be found. The main issue, I think, is that you're expecting it to behave like the system bus or session bus on Linux – but QDBusServer does not actually implement a bus, it is only a peer-to-peer server very similar to the underlying TCP socket. That is, it only produces a QDBusConnection whenever a client connects:

    void QDBusServer::newConnection(const QDBusConnection &connection)

    This signal is emitted when a new client connection connection is established to the server.

    (This looks to me very similar to the TCP or AF_UNIX accept() model.)

    Which hopefully makes it even clearer that even if the server process were able to connect to itself, that connection would not allow it to talk to the client process at all – it would only be a useless loopback connection for the server talking to itself, while the true client connection would be a different QDBusConnection object.

    Also, you mentioned that the code is built twice, but all the ifdefs make it much harder to see whether you're truly building the correct code in the correct .exe, and frankly I think that's a bad idea given that more than half of the code is ifdef'd. At this point just write two separate programs, i.e. fully separate client.cpp and server.cpp.


    (Side note: If this actually were a 'bus' class, then it would have no business being hosted in the same process as the RPC service. The job of a bus – which is normally hosted by 'dbus-daemon' or 'dbus-broker' programs – is primarily to allow communications between its clients, not between a client and the bus itself. A bus that were to host non-bus-operation-related RPCs would be really messy architecture.)