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 原子操作,上面几种方法中, 综合考虑利用 ...
随机推荐
- UVa 714 (二分) Copying Books
首先通过二分来确定这种最大值最小的问题. 假设每个区间的和的最大值为x,那么只要判断的时候只要贪心即可. 也就是如果和不超过x就一直往区间里放数,否则就开辟一个新的区间,这样来判断是否k个区间容得下这 ...
- modeler与activiti进行整合
整合Activiti Modeler到业务系统(或BPM平台) http://www.kafeitu.me/activiti/2013/03/10/integrate-activiti-modeler ...
- hibernate中使用fetch来决策性能方案
什么时候用子查询,或者连接查询 一般多个数据的对应用子查询,单一行的数据用连接 (若要查询每个学生分别学了什么课程 ,若要fetch=join.fetch=select) 则是这种情况 Hiberna ...
- 10327 - Flip Sort
原文链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&pa ...
- android 单独编译某个模块
第一次下载好Android源代码工程后,我们通常是在Android源代码工程目录下执行make命令,经过漫长的等待之后,就可以得到Android系统镜像system.img了.以后如果我们修改了And ...
- 用vs2012的命令利用xsd文件生成对应的C#类,把xml的string类型映射到生成的类
输入命令: xsd d:\TDDOWNLOAD\atom-author-link.xsd /c /language:C# /outputdir:d:\ 含义: 将d:\TDDOWNLOAD\atom- ...
- jQuery 实现Bootstrap Chart 图表
很多时候我们制作报表的时候需要图表,如果你使用bootstrap开发你的网站,如果你需要使用图表,那么最简单的方法就是就是使用bootstrap 的chart.js来实现图表,下面介绍方法 1.引入c ...
- GreenDao官方文档翻译(下)
第五篇 查询 查询会返回符合某些特定标准的实体.你可以使用原始的SQL定制查询语句,或者更好的方式:使用GreenDao的QueryBuilder API.该查询也支持lazy-loading的结果集 ...
- Android intent-filter 简单用法
对电话拨号盘的过滤,mainfest配置文件中Activity如下配置: <activity Android:name=".TestActivity" android:lab ...
- Java IO读写大文件的几种方式及测试
读取文件大小:1.45G 第一种,OldIO: public static void oldIOReadFile() throws IOException{ BufferedReader br = n ...