2014-11-02 5 views
2

Я пытаюсь написать класс SingleApplication, который позволит только одному экземпляру программы работать. Я использую это, используя QSharedMemoryQSharedMemory :: create() issue

Программа работает нормально, если только я не использую ключ со значением "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!"; 
     } 
    } 
} 
+0

Что конкретная ошибка? – Oncaphillis

+0

@Oncaphillis 'sharedMemory.create()' просто возвращает false. Даже в первый раз программа запускается. –

+0

Может ли быть, что сегмент разделяемого сегмента «42» уже используется другим процессом/потоком? Я только сейчас из linux, и ключи - это просто цифры, которые могут быть одинаковыми для разных процессов. Однако есть помощники, которые должны помочь вам сохранить их уникальными. Попробуйте инструмент, в котором перечислены сегменты currenr shm. – Oncaphillis

ответ

1

Я DRO полностью личная концепция приложения, реализованная вами с нуля. qt-solutions repository содержит класс 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; 
     } 
    } 

} 

Отказ от ответственности: это просто псевдо-код, и я никогда не испытывал это, на самом деле, даже не пытался скомпилировать его сам!

2

Предлагаю следующее решение. Он протестирован, но он не поддерживает отправку сообщений между экземплярами. И он устраняет некоторые ошибки вашего решения. Потому что недостаточно просто проверить память. Вам необходимо защитить создание разделяемой памяти.

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(); 
} 
+1

Насколько я понял, ОП интересовался «магическим числом», а не блокировкой, и почему это не работает. Блокировка также не решит проблему создания, может просто заблокировать или переместиться (в зависимости от используемой функции блокировки), но в любом случае внешний процесс или поток по-прежнему будут поддерживать общую память, если OP не должен задерживаться на ней , – lpapp

+0

@lpapp В этом случае он мог бы использовать что-то вроде проводника процессов, чтобы узнать, какое настоящее имя имеет свою общую память, и проверить, занят ли он. Для меня оригинальный вопрос звучит странно. Я просто указываю на ошибку и предлагаю использовать хэши для предотвращения столкновений. –

+0

Для этого вы можете использовать метод nativeKey(), но я предполагаю, что что-то в фоновом режиме сделало эту общую память зарезервированной, если OP не совершила глупых ошибок. :) Если это первый, так что что-то зарезервировало разделяемую память с этой строкой, то, к сожалению, это несложная удача, и Qt не может этого сделать. Кроме того, концепция OP будет разбита на системы без разделяемой памяти. – lpapp