使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(*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 ;
}

Qt实现应用程序单实例运行--LocalServer方式的更多相关文章

  1. DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法

    原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...

  2. C++实现程序单实例运行的两种方式

    简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...

  3. c#设计应用程序单实例运行

    利用WindowsFormsApplicationBase的IsSingleInstance来控制应用程序只能单实例运行. [DllImport("user32.dll", Ent ...

  4. linux保证程序单实例运行

    static int proc_detect(const char *procname){ char filename[100] = {0}; sprintf(filename, "%s/% ...

  5. WinForm 登录窗体 + 单实例运行

    关于怎么在winform里增加登录窗体或者如何让winform程序单实例运行网上都很多例子. 然而两者结合起来呢? //Program.cs static class Program { public ...

  6. Winform 单实例运行

    Winform 单实例运行 前言 前两天在博客园看到<如何防止程序多次运行>,文章写的很好,最后还留下一个问题给我们思考.关于Winform的防止多次运行,曾经也想研究过,但是后来工作上没 ...

  7. Windows进程单实例运行

    场景         Windows进程单实例运行,如果有进程没有退出,继续等待,直到进程完全退出,才会进入下一个实例 HANDLE pHandle = NULL; do  {  pHandle = ...

  8. c# 单实例运行

    /// <summary> /// 单实例运行程序 /// </summary> static void SingleInstanceRun() { bool isAppRun ...

  9. C#实现单实例运行

    C#实现单实例运行的方法,也有多种,比如利用 Process 查找进程的方式,利用 API findwindow 查找窗体的方式,还有就是 利用 Mutex 原子操作,上面几种方法中, 综合考虑利用 ...

随机推荐

  1. BZOJ 1003 物流运输

    最短路+dp. #include<iostream> #include<cstdio> #include<cstring> #include<algorith ...

  2. 解决mysql中文乱码(总)

    转自:http://hi.baidu.com/cnvienna/blog/item/2e99efdfd94fc61862279801.html MySQL会出现中文乱码的原因不外乎下列几点: .ser ...

  3. HDU 5038 Grade

    解题思路:这题最关键的是要读懂题意,If not all the value are the same but the frequencies of them are the same, there ...

  4. ProgressBar 各种样式

    多式样ProgressBar 普通圆形ProgressBar 该类型进度条也就是一个表示运转的过程,例如发送短信,连接网络等等,表示一个过程正在执行中. 一般只要在XML布局中定义就可以了. < ...

  5. android动画学习

    android动画学习   转载自:http://www.open-open.com/lib/view/open1329994048671.html 3.0以前,android支持两种动画模式,twe ...

  6. 在SQL语言中,join什么时候用,什么时候不用啊?请高手举例解释一下。谢谢

    JOIN 在内连接时,可以不使用,其它类型连接必须使用.如SELECT * FROM TABLEA INNER JOIN TABLEB ON A.ID=B.ID可以这样写:SELECT * FROM ...

  7. delete之后千万要记得将指针置空,即勿重复delete!!!

    下面这段代码有什么问题吗?(Arduino上运行) class C{ public: C(){ ptr = ]; } ~C(){ if(ptr!=NULL)delete [] ptr; } void ...

  8. 代理抓取RSS信息

    最近工作很闲,就自己写了一个可以看RSS订阅的网站.话说,RSS阅读器到处都是,随便下一个就可以了,为什么还去做一个网站形式的呢?作为一个热(xian)爱(de)前(dan)端(teng)的程序员,我 ...

  9. Linux目录初识

    / 根目录 /bin 存放必要的命令 /boot 存放内核以及启动所需的文件/dev 存放设备文件 /etc 存放系统配置文件 /home 普通用户的宿主目录,用户数据存放在其主目录中 /lib 存放 ...

  10. 上传Test Result和attachment到ALM

    之前在HP的时候用ALM,还是很好用的功能很强大的一个测试管理工具,当时用C#依照ALM的API实现了一个上传测试结果的程序,现在贴出来: 这个程序的使用方式很自由,使得ALM几乎可以和所有测试工具做 ...