您的位置:首页 > 移动开发

Qt实现单实例(QSharedMemory缺陷以及用QLocalSocket QLocalServer实现QSingleApplication)

2015-02-26 21:19 405 查看
使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

/article/8480962.html

上面的方案实际操作过程中出现很多问题:

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐