QSharedMemory :: create()问题

时间:2014-11-02 21:19:29

标签: c++ qt shared-memory qtcore qsharedmemory

我正在尝试编写一个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!";
        }
    }
}

2 个答案:

答案 0 :(得分: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 :(得分:1)

我会放弃你自己从头开始实现的整个应用程序概念。 qt-solutions repository包含了移植到Qt 5的QtSingleApplication类。你应该能够使用它。在我的拙见中重新发明轮子几乎没有任何意义。

你是否仍然坚持自己这样做,虽然你的想法似乎有点奇怪,一开始将密钥传递给构造函数而不是透明地管理类内部,这可能是你的案例的解决方法使解决方案更加健壮:

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;
        }
    }

}

免责声明:这只是伪代码,我从未测试过,实际上,甚至没有尝试自己编译它!