一、实现方案
目前使 Qt 运行一个实例有如下几种方式:
1.QSharedMemory
使用共享内存,当第二个进程启动时,判断内存区数据是否建立,如有,则退出;这种方式有弊端,在程序发生崩溃时,未及时清除共享区数据,导致程序不能正常启动。
2.文件锁
在程序运行的时候就在目录下创建一个文件,当程序运行时就判断这个文件是否存在,如果存在说明程序已经在运行。其本质与 QSharedMemory 相同。
3.利用QLocalServer
要用到Qt的QLocalSocket,QLocalServer类,这两个类从接口上看和网络通信socket没有区别,但是它并不是真正的网络API,只是模仿了而已。这两个类在Unix/Linux系统上采用Unix域socket实现,而在Windows上则采用有名管道(named pipe)来实现
4.QtSingleApplication
使用 Qt 扩展库 QtSingleApplication,能很好的解决这个问题。
QSingleApplication 是 Qt 提供的一个 solution ,它不包含在 Qt 的 library 中。遵循 LGPL 协议。Qt 欢迎里面有例子。
二、实现代码
下面给出了 LocalServer 方式的实现代码。
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;
}