Qt 實現應用程序單實例運行


一、實現方案

目前使 Qt 運行一個實例有如下幾種方式:

1.QSharedMemory

使用共享內存,當第二個進程啟動時,判斷內存區數據是否建立,如有,則退出;這種方式有弊端,在程序發生崩潰時,未及時清除共享區數據,導致程序不能正常啟動。

2.文件鎖

在程序運行的時候就在目錄下創建一個文件,當程序運行時就判斷這個文件是否存在,如果存在說明程序已經在運行。其本質與 QSharedMemory 相同。

3.利用QLocalServer

參考:
Qt實現應用程序單實例運行–LocalServer方式
讓QT只運行一個實例

4.QtSingleApplication

使用 Qt 擴展庫 QtSingleApplication,能很好的解決這個問題。

QSingleApplication 是 Qt 提供的一個 solution ,它不包含在 Qt 的 library 中。遵循 LGPL 協議。Qt 歡迎里面有例子。


二、實現代碼

下面給出了 LocalServer 方式的實現代碼。

SingleApplication.h:

#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H

#include <QObject>
#include <QApplication>
#include <QtNetwork/QLocalServer>
#include <QWidget>

class SingleApplication : public QApplication {
        Q_OBJECT
    public:
        SingleApplication(int &argc, char **argv);

        bool isRunning();                // 是否已經有實例在運行
        QWidget *w;                        // MainWindow指針

    private slots:
        // 有新連接時觸發
        void _newLocalConnection();

    private:
        // 初始化本地連接
        void _initLocalConnection();
        // 創建服務端
        void _newLocalServer();
        // 激活窗口
        void _activateWindow();

        bool _isRunning;                // 是否已經有實例在運行
        QLocalServer *_localServer;     // 本地socket Server
        QString _serverName;            // 服務名稱
};

#endif // SINGLEAPPLICATION_H

SingleApplication.cpp:

#include "SingleApplication.h"
#include <QtNetwork/QLocalSocket>
#include <QFileInfo>

#define TIME_OUT                (500)    // 500ms

SingleApplication::SingleApplication(int &argc, char **argv)
    : QApplication(argc, argv)
    , w(NULL)
    , _isRunning(false)
    , _localServer(NULL) {

    // 取應用程序名作為LocalServer的名字
    _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();

    _initLocalConnection();
}


////////////////////////////////////////////////////////////////////////////////
// 說明:
// 檢查是否已經有一個實例在運行, true - 有實例運行, false - 沒有實例運行
////////////////////////////////////////////////////////////////////////////////
bool SingleApplication::isRunning() {
    return _isRunning;
}

////////////////////////////////////////////////////////////////////////////////
// 說明:
// 通過socket通訊實現程序單實例運行,監聽到新的連接時觸發該函數
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_newLocalConnection() {
    QLocalSocket *socket = _localServer->nextPendingConnection();
    if(socket) {
        socket->waitForReadyRead(2*TIME_OUT);
        delete socket;

        // 其他處理,如:讀取啟動參數

        _activateWindow();
    }
}

////////////////////////////////////////////////////////////////////////////////
// 說明:
// 通過socket通訊實現程序單實例運行,
// 初始化本地連接,如果連接不上server,則創建,否則退出
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_initLocalConnection() {
    _isRunning = false;

    QLocalSocket socket;
    socket.connectToServer(_serverName);
    if(socket.waitForConnected(TIME_OUT)) {
        fprintf(stderr, "%s already running.\n",
                _serverName.toLocal8Bit().constData());
        _isRunning = true;
        // 其他處理,如:將啟動參數發送到服務端
        return;
    }

    //連接不上服務器,就創建一個
    _newLocalServer();
}

////////////////////////////////////////////////////////////////////////////////
// 說明:
// 創建LocalServer
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_newLocalServer() {
    _localServer = new QLocalServer(this);
    connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
    if(!_localServer->listen(_serverName)) {
        // 此時監聽失敗,可能是程序崩潰時,殘留進程服務導致的,移除之
        if(_localServer->serverError() == QAbstractSocket::AddressInUseError) {
            QLocalServer::removeServer(_serverName); // <-- 重點
            _localServer->listen(_serverName); // 再次監聽
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// 說明:
// 激活主窗口
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_activateWindow() {
    if(w) {
        w->show();
        w->raise();
        w->activateWindow(); // 激活窗口
    }
}

調用示例:

#include "MainWindow.h"
#include "SingleApplication.h"

int main(int argc, char *argv[]) {
    SingleApplication a(argc, argv);
    if(!a.isRunning()) {
        MainWindow w;
        a.w = &w;

        w.show();

        return a.exec();
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM