Qt實現應用程序單實例運行--LocalServer方式


使Qt應用程序能夠單實例運行的典型實現方法是使用共享內存實現。該方法實現簡單,代碼簡潔。

但有一個致命缺陷:共享內存(QSharedMemory)實現的單程序運行,當運行環境是UNIX時,並且程序不幸崩潰,會導致共享內存無法釋放,從而無法重新運行程序!

 

所以應該尋找其他的使Qt應用程序能夠單實例運行的方案。於是找到LocalSocket和LocalServer通訊方案(據說Qt官方商業版的QSingleApplication的原理好像跟這個差不多)。

“要用到Qt的QLocalSocket,QLocalServer類,這兩個類從接口上看和網絡通信socket沒有區別,但是它並不是真正的網絡API,只是模仿了而已。這兩個類在Unix/Linux系統上采用Unix域socket實現,而在Windows上則采用有名管道(named pipe)來實現。”

參見:

http://www.oschina.net/code/snippet_54100_629

http://blog.csdn.net/qq19831030qq/article/details/6199896

 

上面的方案實際操作過程中出現很多問題:

QString serverName = QCoreApplication::applicationName(); //獲取到的serverName為空

為了解決上面的問題,最直接的測試方法是先手動指定一個serverName,

然后將QFile::remove(m_localServer->serverName());改成QFile::remove(serverName);

 

當手動指定serverName時,習慣上將serverName設為當前的應用程序名,在linux下這又導致下面的問題

QFile::remove(m_localServer->serverName()); //執行刪除失敗(實際測試發現失敗原因是:嘗試刪除應用程序文件自身!)

 

 上面的兩個問題導致Qt應用程序無法單實例運行。

 

后來終於在StackOverflow上面看到解決方案:使用QLocalServer::removeServer()刪除LocalServer名

下面是實現代碼:

頭文件:

 1 #ifndef SINGLEAPPLICATION_H
 2 #define SINGLEAPPLICATION_H
 3 
 4 #include <QObject>
 5 #include <QApplication>
 6 #include <QtNetwork/QLocalServer>
 7 #include <QWidget>
 8 
 9 class SingleApplication : public QApplication {
10         Q_OBJECT
11     public:
12         SingleApplication(int &argc, char **argv);
13 
14         bool isRunning();                // 是否已經有實例在運行
15         QWidget *w;                        // MainWindow指針
16 
17     private slots:
18         // 有新連接時觸發
19         void _newLocalConnection();
20 
21     private:
22         // 初始化本地連接
23         void _initLocalConnection();
24         // 創建服務端
25         void _newLocalServer();
26         // 激活窗口
27         void _activateWindow();
28 
29         bool _isRunning;                // 是否已經有實例在運行
30         QLocalServer *_localServer;     // 本地socket Server
31         QString _serverName;            // 服務名稱
32 };
33 
34 #endif // SINGLEAPPLICATION_H

 CPP文件:

 1 #include "SingleApplication.h"
 2 #include <QtNetwork/QLocalSocket>
 3 #include <QFileInfo>
 4 
 5 #define TIME_OUT                (500)    // 500ms
 6 
 7 SingleApplication::SingleApplication(int &argc, char **argv)
 8     : QApplication(argc, argv)
 9     , w(NULL)
10     , _isRunning(false)
11     , _localServer(NULL) {
12 
13     // 取應用程序名作為LocalServer的名字
14     _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
15 
16     _initLocalConnection();
17 }
18 
19 
20 ////////////////////////////////////////////////////////////////////////////////
21 // 說明:
22 // 檢查是否已經有一個實例在運行, true - 有實例運行, false - 沒有實例運行
23 ////////////////////////////////////////////////////////////////////////////////
24 bool SingleApplication::isRunning() {
25     return _isRunning;
26 }
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 // 說明:
30 // 通過socket通訊實現程序單實例運行,監聽到新的連接時觸發該函數
31 ////////////////////////////////////////////////////////////////////////////////
32 void SingleApplication::_newLocalConnection() {
33     QLocalSocket *socket = _localServer->nextPendingConnection();
34     if(socket) {
35         socket->waitForReadyRead(2*TIME_OUT);
36         delete socket;
37 
38         // 其他處理,如:讀取啟動參數
39 
40         _activateWindow();
41     }
42 }
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 // 說明:
46 // 通過socket通訊實現程序單實例運行,
47 // 初始化本地連接,如果連接不上server,則創建,否則退出
48 ////////////////////////////////////////////////////////////////////////////////
49 void SingleApplication::_initLocalConnection() {
50     _isRunning = false;    
51 
52     QLocalSocket socket;
53     socket.connectToServer(_serverName);
54     if(socket.waitForConnected(TIME_OUT)) {
55         fprintf(stderr, "%s already running.\n",
56                 _serverName.toLocal8Bit().constData());
57         _isRunning = true;
58         // 其他處理,如:將啟動參數發送到服務端
59         return;
60     }
61 
62     //連接不上服務器,就創建一個
63     _newLocalServer();
64 }
65 
66 ////////////////////////////////////////////////////////////////////////////////
67 // 說明:
68 // 創建LocalServer
69 ////////////////////////////////////////////////////////////////////////////////
70 void SingleApplication::_newLocalServer() {
71     _localServer = new QLocalServer(this);
72     connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
73     if(!_localServer->listen(_serverName)) {
74         // 此時監聽失敗,可能是程序崩潰時,殘留進程服務導致的,移除之
75         if(_localServer->serverError() == QAbstractSocket::AddressInUseError) {
76             QLocalServer::removeServer(_serverName); // <-- 重點 77             _localServer->listen(_serverName); // 再次監聽 78         }
79     }
80 }
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 // 說明:
84 // 激活主窗口
85 ////////////////////////////////////////////////////////////////////////////////
86 void SingleApplication::_activateWindow() {
87     if(w) {
88         w->show();
89         w->raise();
90         w->activateWindow(); // 激活窗口
91     }
92 }

調用示例:

 1 #include "MainWindow.h"
 2 #include "SingleApplication.h"
 3 
 4 int main(int argc, char *argv[]) {
 5     SingleApplication a(argc, argv);
 6     if(!a.isRunning()) {
 7         MainWindow w;
 8         a.w = &w;
 9 
10         w.show();
11 
12         return a.exec();
13     }
14     return 0;
15 }

 


免責聲明!

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



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