zoukankan      html  css  js  c++  java
  • 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.
    ",
    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 }
    作者: js2854
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Android:JNI之Java和C层的相互调用及多线程的回调实现
    高通sdm845_la2.0源码编译及使用QFIL刷机
    git常用指令
    Bouml快速使用指南
    Linux内核数据结构之kfifo详解
    输入系统:进程间双向通信(socketpair+binder)
    Android : 跟我学Binder --- (6) JAVA实现
    【LeetCode】167. Two Sum II
    【LeetCode】1. Two Sum
    【LeetCode】206. Reverse Linked List
  • 原文地址:https://www.cnblogs.com/js2854/p/qt-single-app.html
Copyright © 2011-2022 走看看