一、實現方案
目前使 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;
}