2

我正在尝试编写一个SingleApplication只允许程序的一个实例运行的类。我正在使用QSharedMemory

该程序运行良好,除非我使用带有 value 的键"42"。我做错了什么吗?这是未定义的行为吗?

主文件

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

单应用程序.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;

};

单应用程序.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!";
        }
    }
}
4

2 回答 2

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();
}
于 2014-11-03T09:02:57.663 回答
1

我会放弃你个人从头开始实现的整个自己的单一应用程序概念。qt-solutions 存储库也包含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;
        }
    }

}

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

于 2014-11-03T04:36:22.193 回答