使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应用程序无法单实例运行。
#include "singleapplication.h"
#include <QDataStream>
#define TIME_OUT 500
SingleApplication::SingleApplication(int &argc, char **argv) :
QApplication(argc, argv)
,w(NULL)
,_isRunning(false)
,_localServer(NULL)
{
_serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
_initLocalConnection();
}
bool SingleApplication::isRunning()
{
return _isRunning;
}
void SingleApplication::_initLocalConnection()
{
_isRunning = false;
QLocalSocket socket;
socket.connectToServer(_serverName);
if (socket.waitForConnected(TIME_OUT)) {
_isRunning = true;
return;
}
_newLocalServer();
}
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->setWindowState(w->windowState() & ~Qt::WindowMinimized);
w->show();
w->raise();
w->activateWindow();
}
}
void SingleApplication::_newLocalConnection()
{
QLocalSocket *socket = _localServer->nextPendingConnection();
if (socket) {
socket->waitForReadyRead(2 * TIME_OUT);
delete socket;
_activateWindow();
}
}
头文件代码如下:
#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include <QtWidgets>
class SingleApplication : public QApplication
{
Q_OBJECT
public:
explicit SingleApplication(int &argc, char **argv);
bool isRunning();
QWidget *w;
signals:
public slots:
void _newLocalConnection();
private:
void _initLocalConnection();
void _newLocalServer();
void _activateWindow();
QLocalServer *_localServer;
QString _serverName;
bool _isRunning;
};
#endif // SINGLEAPPLICATION_H