2014-11-02 510 views
2

我想写一个SingleApplication类,它只允许程序的一个实例运行。我正在使用QSharedMemoryQSharedMemory :: create()问题

该程序工作正常,除非我使用的值为"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 2014-11-02 22:02:39

+0

@Oncaphillis'sharedMemory.create()'只是返回false。即使是第一次运行程序。 – 2014-11-02 22:12:40

+0

难道共享内存段“42”已被其他进程/线程使用吗?我现在只从shm linux和keys只是不同的进程可能是相同的数字。然而,有些帮手会帮助你保持独特。尝试一个列出currenr shm段的工具。 – Oncaphillis 2014-11-02 22:15:38

回答

1

我会DRO p自己从零开始实施的整个自己的单一应用程序概念。 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对“幻数”感兴趣,而不是锁定,为什么这不起作用。锁定不会解决创建问题,可能只是阻止或继续(取决于所使用的锁定功能),但无论在哪种情况下,外部进程或线程仍将保持共享内存,而OP不会让它保留。 – lpapp 2014-11-03 11:28:16

+0

@lpapp在这种情况下,他可以使用类似进程资源管理器的东西来查看他的共享内存是什么实名,并检查它是否忙碌。对我来说,原始问题听起来很奇怪。我只是指出了错误,并建议使用散列来防止碰撞。 – 2014-11-03 11:39:42

+0

您可以使用nativeKey()方法,但是我认为后台的某些东西会使共享内存保留,除非OP犯了一些愚蠢的错误。 :)如果是前者,那么保留与该字符串共享内存的东西,那么不幸的是,这是一个艰难的运气,并且Qt可以做的并不多。另外,OP的概念将在没有共享内存的系统上被打破。 – lpapp 2014-11-03 11:41:37

相关问题