文章标题 原创 翻译 转载 文章内容 Qt 客户端应用程序多开要注意的问题 很多客户端程序同一台电脑只允许开启一个进程,这个是很有必要的。那将一个原本单开的程序改为允许多开要注意些什么呢? # 本地配置 如果多个进程读写同一个配置会造成混乱(只读的配置除外),所以要控制同一个目录不允许开两个。 # 进程名 如果两个进程的进程名相同,当通过进程名做某件事的时候会出问题(通过进程名关闭,显示程序),所以要保证进程名不一样。 当我们把某些信息存储在系统的一些标准目录下时(如AppData低权限读写目录)如果进程名相同,那么两个进程会读写同一个文件也会造成问题。 # 全局信息 比如客户端连接的是qpid broker,当与服务端通信的时候需要指定消息队列,如果两个进程用的都是同一个消息队列,很明显会造成其中一个客户端收不到消息,此时可以在消息队列名后面加一个当前进程ID来解决这个问题 下面这段代码可以用来保证同一个程序只有运行一个进程: RunGuard.h ``` #pragma once #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) }; ``` RunGuard.cpp ``` #include "RunGuard.h" #include <QCryptographicHash> #include <QDebug> 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; } class MemLockGuard { public: MemLockGuard(QSystemSemaphore &lock) : lock_(lock) { lock_.acquire(); } ~MemLockGuard() { lock_.release(); } private: QSystemSemaphore &lock_; }; } RunGuard::RunGuard(const QString& key) : key(key) , memLockKey(generateKeyHash(key, "_memLockKey_")) , sharedmemKey(generateKeyHash(key, "_sharedmemKey_")) , sharedMem(sharedmemKey) , memLock(memLockKey, 1) { MemLockGuard lock(memLock); { QSharedMemory fix(sharedmemKey); fix.attach(); } } RunGuard::~RunGuard() { release(); } bool RunGuard::isAnotherRunning() { if (sharedMem.isAttached()) { return false; } MemLockGuard lock(memLock); const bool isRunning = sharedMem.attach(); if (isRunning) { sharedMem.detach(); } return isRunning; } bool RunGuard::tryToRun() { qDebug() << "try run key:" << key; // Extra check if (isAnotherRunning()) { qDebug() << "try run failed, key:" << key; return false; } bool result = false; { MemLockGuard lock(memLock); result = sharedMem.create(sizeof(quint64)); } if (!result) { qDebug() << "try run failed, create error:" << sharedMem.errorString(); release(); } return result; } void RunGuard::release() { MemLockGuard lock(memLock); if (sharedMem.isAttached()) { sharedMem.detach(); } } ``` 多开保证目录和进程名不同: ``` QString programPath = QString::fromStdWString(AnsiToUnicode(argv[0])); int lastSlash = programPath.replace('/', '\\').lastIndexOf('\\'); RunGuard dirGuard(programPath.mid(0, lastSlash).toLower()); RunGuard nameGuard(programPath.mid(lastSlash + 1).toLower()); if (!dirGuard.tryToRun() || !nameGuard.tryToRun()) { // 通过进程名打开先前进程的主窗口 ShowMainWindow(programPath.mid(lastSlash + 1).toStdWString().c_str()); return 0; } ``` 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交