22
Nov
0

C++ – Qt – SingleApplication – Single App Instance

How to create an application that allows only one instance at a time. Here is my solution inspired from : http://www.qtcentre.org/wiki/index.php?title=SingleApplication

Introduction

I change several stuffs from the QtCentre code. First it is “process-safe” (so several programs can start at the same time, there is no problem), Second, it used the shared memory and allows to send several messages (in the original code only one message can be written in the memory).

The code

Here is the class :

// "single_application.h"

#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H

#include <QApplication>
#include <QSharedMemory>
#include <QStringList>

class SingleApplication : public QApplication
{
        Q_OBJECT
public:
        SingleApplication(int &argc, char *argv[], const QString uniqueKey);

        bool alreadyExists() const{
            return bAlreadyExists;
        }
        bool isMasterApp() const{
            return !alreadyExists();
        }

        bool sendMessage(const QString &message);

public slots:
        void checkForMessage();

signals:
        void messageAvailable(const QStringList& messages);

private:
        bool bAlreadyExists;
        QSharedMemory sharedMemory;
};
#endif // SINGLE_APPLICATION_H
// "single_application.cpp"

#include <QTimer>
#include <QByteArray>

#include "singleapplication.h"

SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv)
{
    sharedMemory.setKey(uniqueKey);

    // when  can create it only if it doesn't exist
    if (sharedMemory.create(5000))
    {
        sharedMemory.lock();
        *(char*)sharedMemory.data() = '\0';
        sharedMemory.unlock();

        bAlreadyExists = false;

        // start checking for messages of other instances.
        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(checkForMessage()));
        timer->start(200);
    }
    // it exits, so we can attach it?!
    else if (sharedMemory.attach()){
        bAlreadyExists = true;
    }
    else{
        // error
    }

}

// public slots.

void SingleApplication::checkForMessage()
{
    QStringList arguments;

    sharedMemory.lock();
    char *from = (char*)sharedMemory.data();

    while(*from != '\0'){
        int sizeToRead = int(*from);
        ++from;

        QByteArray byteArray = QByteArray(from, sizeToRead);
        byteArray[sizeToRead] = '\0';
        from += sizeToRead;

        arguments << QString::fromUtf8(byteArray.constData());
    }

    *(char*)sharedMemory.data() = '\0';
    sharedMemory.unlock();

    if(arguments.size()) emit messageAvailable( arguments );
}

// public functions.

bool SingleApplication::sendMessage(const QString &message)
{
    //we cannot send mess if we are master process!
    if (isMasterApp()){
        return false;
    }

    QByteArray byteArray;
    byteArray.append(char(message.size()));
    byteArray.append(message.toUtf8());
    byteArray.append('\0');

    sharedMemory.lock();
    char *to = (char*)sharedMemory.data();
    while(*to != '\0'){
        int sizeToRead = int(*to);
        to += sizeToRead + 1;
    }

    const char *from = byteArray.data();
    memcpy(to, from, qMin(sharedMemory.size(), byteArray.size()));
    sharedMemory.unlock();

    return true;
}

Example

The main has to look like this :

#include <QtGui/QApplication>
#include "mainwindow.h"

#include "singleapplication.h"

int main(int argc, char *argv[])
{
    SingleApplication a(argc, argv,"myUniqueKey");

    if(a.alreadyExists()){
        a.sendMessage("I am another instance");
        a.sendMessage("I want to send several message like argv if needed");

        return 0;
    }

    MainWindow w;
    w.show();
    a.connect(&a,SIGNAL(messageAvailable(QStringList)),&w,SLOT(ReceiveMessageFromOtherInstance(QStringList)));

    return a.exec();
}

And we can imagine something like :

public slots:
    void ReceiveMessageFromOtherInstance(const QStringList& mess){
        QMessageBox::warning(this,"Hey","Some Apps send :\n"+mess.join("\n"));
    }

But most of time it is useful to pass argv from an app to another.

Enjoyed reading this post?
Subscribe to the RSS feed and have all new posts delivered straight to you.

Comments are closed.

Celadon theme by the Themes Boutique