大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > Qt实现应用程序单实例运行--LocalServer方式

Qt实现应用程序单实例运行--LocalServer方式

关键词:应用程序单实例运行Qt  阅读(2476) 赞(13)

[摘要]本文是对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名

下面是实现代码:

头文件:

 #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

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;
 }

相关C++技巧推荐

    暂时没有相关推荐



    相关评论