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名
下面是实现代码:
头文件:
#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方式的更多相关文章
- DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法
原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...
- C++实现程序单实例运行的两种方式
简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...
- c#设计应用程序单实例运行
利用WindowsFormsApplicationBase的IsSingleInstance来控制应用程序只能单实例运行. [DllImport("user32.dll", Ent ...
- linux保证程序单实例运行
static int proc_detect(const char *procname){ char filename[100] = {0}; sprintf(filename, "%s/% ...
- WinForm 登录窗体 + 单实例运行
关于怎么在winform里增加登录窗体或者如何让winform程序单实例运行网上都很多例子. 然而两者结合起来呢? //Program.cs static class Program { public ...
- Winform 单实例运行
Winform 单实例运行 前言 前两天在博客园看到<如何防止程序多次运行>,文章写的很好,最后还留下一个问题给我们思考.关于Winform的防止多次运行,曾经也想研究过,但是后来工作上没 ...
- Windows进程单实例运行
场景 Windows进程单实例运行,如果有进程没有退出,继续等待,直到进程完全退出,才会进入下一个实例 HANDLE pHandle = NULL; do { pHandle = ...
- c# 单实例运行
/// <summary> /// 单实例运行程序 /// </summary> static void SingleInstanceRun() { bool isAppRun ...
- C#实现单实例运行
C#实现单实例运行的方法,也有多种,比如利用 Process 查找进程的方式,利用 API findwindow 查找窗体的方式,还有就是 利用 Mutex 原子操作,上面几种方法中, 综合考虑利用 ...
随机推荐
- 推荐 10 款最好的 Python IDE
简述 Python 非常易学,强大的编程语言.Python 包括高效高级的数据结构,提供简单且高效的面向对象编程. Python 的学习过程少不了 IDE 或者代码编辑器,或者集成的开发编辑器(IDE ...
- 查找所有含有表名(abc)的存储过程 执行脚本
SELECT obj.Name , sc.TEXT FROM syscomments sc INNER JOIN sysobjects obj ON sc.Id = obj.ID WHERE sc.T ...
- Book 最短路算法
用HDU2544整理一下最近学的最短路算法 1.Dijkstra算法 原理:集合S表示已经找到最短路径的点,d[]表示当前各点到源点的距离 初始时,集合里面只有源点,当每个点u进入集合S时,用d[u] ...
- 淘宝主搜索离线集群完成Hadoop 2
淘宝搜索离线dump集群(hadoop&hbase)2013进行了几次重大升级,本文中将这些升级的详细过程.升级中所遇到的问题以及这些问题的解决方案分享给大家.至此,淘宝主搜索离线集群完全进入 ...
- MySQL集群的可行方案
如果单MySQL的优化始终还是顶不住压力时,这个时候我们就必须考虑MySQL的高可用架构(很多同学也爱说成是MySQL集群)了,目前可行的方案有: 一.MySQL Cluster优势:可用性非常高,性 ...
- (三)spark集群DHCP IP变化后的处理
学校的网比较特殊(本人比较菜),需要DHCP获得地址,那么有时候IP被占用了,应该如何应对呢? 1)修改/etc/hosts 2) 修改spark-env.sh 里的 Master 地址 ,Maste ...
- 【英语】Bingo口语笔记(10) - 常见词汇的缩读
- 在Android中使用并发来提高速度和性能
Android框架提供了很实用的异步处理类.然而它们中的大多数在一个单一的后台线程中排队.当你需要多个线程时你是怎么做的? 众所周知,UI更新发生在UI线程(也称为主线程).在主线程中的任何操作都会阻 ...
- Word2003使用VBA教程
[正文] 注:本文中所有vba代码都是储存在doc中,而非normal.dot 1.打开一个.doc文档 2.按ALT+F11 3.左侧 Project-插入-模块 4.输入自己的代码,一定要是函数的 ...
- PHPMailer邮件类使用错误分析
PHPMailer配置清单如下: require_once ‘class.phpmailer.php‘; $receiver = ”; $mail = new PHPMailer ( ); $mai ...