Я пытаюсь написать SingleApplication
класс, который позволит запускать только один экземпляр программы. Я реализую это с помощью QSharedMemory
Программа работает нормально, если я не использую ключ со значением "42"
, Что-то не так я делаю? Это неопределенное поведение?
main.cpp
int main(int argc, char *argv[])
{
//QApplication a(argc, argv);
SingleApplication a(argc, argv, "42"); //Does not work with '42'. Will work for any other value.MainWindow w;
w.show();return a.exec();
}
SingleApplication.h
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 checkForMessages();
signals:
//void messageAvailable(const QStringList& messages);
private:
bool bAlreadyExists;
QSharedMemory sharedMemory;
};
SingleApplication.cpp
SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv){
sharedMemory.setKey(uniqueKey);
//Create if one does not exist already
if(sharedMemory.create(5000))
{
qDebug() << "Created!";
bAlreadyExists = false;
}
else{
if(sharedMemory.error() == QSharedMemory::AlreadyExists){
qWarning() << "Program is already running!";
}
}
}
Я предлагаю вам следующее решение. Он протестирован, но не поддерживает отправку сообщений между экземплярами. И это устраняет некоторые ошибки вашего решения. Потому что недостаточно просто проверить память. Вы должны охранять создание общей памяти.
RunGuard.h
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>class RunGuard
{
public:
RunGuard( const QString& key );
~RunGuard();
bool isAnotherRunning();
bool tryToRun();
void release();
private:
const QString key;
const QString memLockKey;
const QString sharedmemKey;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
Q_DISABLE_COPY( RunGuard )
};#endif // RUNGUARD_H
RunGuard.cpp
#include "RunGuard.h"
#include <QCryptographicHash>namespace
{
QString generateKeyHash( const QString& key, const QString& salt )
{
QByteArray data;
data.append( key.toUtf8() );
data.append( salt.toUtf8() );
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
return data;
}
}RunGuard::RunGuard( const QString& key )
: key( key )
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
, sharedMem( sharedmemKey )
, memLock( memLockKey, 1 )
{
QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
RunGuard::~RunGuard()
{
release();
}
bool RunGuard::isAnotherRunning()
{
if ( sharedMem.isAttached() )
return false;
memLock.acquire();
const bool isRunning = sharedMem.attach();
if ( isRunning )
sharedMem.detach();
memLock.release();
return isRunning;
}
bool RunGuard::tryToRun()
{
if ( isAnotherRunning() ) // Extra check
return false;
memLock.acquire();
const bool result = sharedMem.create( sizeof( quint64 ) );
memLock.release();
if ( !result )
{
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if ( sharedMem.isAttached() )
sharedMem.detach();
memLock.release();
}
Я бы отбросил всю концепцию единого приложения, реализованную вами с нуля лично. хранилище qt-решений содержал QtSingleApplication
класс, который был портирован на Qt 5, тоже. Вы должны быть в состоянии использовать это. По моему скромному мнению, нет смысла заново изобретать велосипед.
Если вы по-прежнему настаиваете на том, чтобы сделать это самостоятельно, в то время как ваша идея поначалу кажется немного странной в отношении передачи ключа конструктору, а не прозрачного управления этим внутри класса, это может стать обходным решением для вашего случая сделать Решение более надежное:
SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv)
{
sharedMemory.setKey(uniqueKey);
if (!sharedMemory.create(5000)) {
while (sharedMemory.error() == QSharedMemory::AlreadyExists) {
// Set a new key after some string manipulation
// This just a silly example to have a placeholder here
// Best would be to increment probably, and you could still use
// a maximum number of iteration just in case.
sharedMemory.setKey(sharedMemory.key() + QLatin1String("0"));
// Try to create it again with the new key
sharedMemory.create(5000);
}
if (sharedMemory.error() != QSharedMemory::NoError)
qDebug() << "Could not create the shared memory:" << sharedMemory.errorString();
else
{
qDebug() << "Created!";
bAlreadyExists = false;
}
}
}
Отказ от ответственности: это просто псевдокод, и я никогда не проверял его, даже не пытался скомпилировать сам!