[轉]Qt實現應用程式單例項執行--LocalServer方式
阿新 • • 發佈:2020-07-21
使 Qt 應用程式能夠單例項執行的典型實現方法是使用共享記憶體實現。該方法實現簡單,程式碼簡潔。
但有一個致命缺陷:共享記憶體(QSharedMemory)實現的單程式執行,當執行環境是 UNIX 時,並且程式不幸崩潰,會導致共享記憶體無法釋放,從而無法重新執行程式!
所以應該尋找其他的使 Qt 應用程式能夠單例項執行的方案。於是找到 LocalSocket 和 LocalServer 通訊方案(據說 Qt官方商業版的QSingleApplication的原理好像跟這個差不多)。
“要用到 Qt 的 QLocalSocket,QLocalServer 類,這兩個類從介面上看和網路通訊 socket 沒有區別,但是它並不是真正的網路 API,只是模仿了而已。這兩個類在 Unix/Linux 系統上採用 Unix 域 socket 實現,而在 Windows 上則採用有名管道(named pipe)來實現。”
下面是實現程式碼:
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;
}