Olivier Cléro

QtAppInstanceManager

07/11/2021

Presentation

QtAppInstanceManager is a tool to control how many instances of your Qt5 application are running at the same time, and to send messages between them.

A long time ago, when Qt was still Qt4, there was a module named QtSingleApplication that allowed this kind of behavior. Unfortunately, it is deprecated and not part of Qt5 anymore.

Some alternative solutions emerged since, but nothing was totally satisfying. I decided to build my own implementation, using C++17, Qt5 and local sockets.

You may then build upon this foundation any messaging system or protocol, such as JSON-RPC, for instance (NB: not provided because out of the scope of this library). This is what I did for the company I work.

Usage

The basic usage looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Initialize instance manager to force only one instance running.
oclero::QtAppInstanceManager instanceManager;
instanceManager.setForceSingleInstance(true);

// When another instance will start, it will immediately quit and send its
// arguments to the primary instance.
QObject::connect(&instanceManager,
  &oclero::QtAppInstanceManager::secondaryInstanceMessageReceived,
  &instanceManager,
  [](const unsigned int id, QByteArray const& data) {
    qDebug() << "Secondary instance message received: " << data;
  });

If ever the first instance to launch unexpectly shuts down, one of the secondary instances will immediately try to lock the socket and take of the role of the primary one.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
QObject::connect(&instanceManager,
                 &oclero::QtAppInstanceManager::instanceRoleChanged,
                 &instanceManager,
  [&instanceManager]() {
    if (!instanceManager.isPrimaryInstance() && !instanceManager.isSecondaryInstance()) {
      // There is a short period of time before roles are assigned again.
      qDebug() << "Waiting for new role...";
    } else {
      // Now we have a new role!
      qDebug() << "New role: " << (instanceManager.isPrimaryInstance() ? "Primary" : "Secondary");
    }
  });

Information