使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

上面的方案实际操作过程中出现很多问题:

  1. QString serverName = QCoreApplication::applicationName(); //获取到的serverName为空

为了解决上面的问题,最直接的测试方法是先手动指定一个serverName,

然后将QFile::remove(m_localServer->serverName());改成QFile::remove(serverName);

当手动指定serverName时,习惯上将serverName设为当前的应用程序名,在linux下这又导致下面的问题

  1. QFile::remove(m_localServer->serverName()); //执行删除失败(实际测试发现失败原因是:尝试删除应用程序文件自身!)

上面的两个问题导致Qt应用程序无法单实例运行。

后来终于在StackOverflow上面看到解决方案:使用QLocalServer::removeServer()删除LocalServer名

下面是实现代码:

头文件:

  1. #ifndef SINGLEAPPLICATION_H
  2. #define SINGLEAPPLICATION_H
  3.  
  4. #include <QObject>
  5. #include <QApplication>
  6. #include <QtNetwork/QLocalServer>
  7. #include <QWidget>
  8.  
  9. class SingleApplication : public QApplication {
  10. Q_OBJECT
  11. public:
  12. SingleApplication(int &argc, char **argv);
  13.  
  14. bool isRunning(); // 是否已經有实例在运行
  15. QWidget *w; // MainWindow指针
  16.  
  17. private slots:
  18. // 有新连接时触发
  19. void _newLocalConnection();
  20.  
  21. private:
  22. // 初始化本地连接
  23. void _initLocalConnection();
  24. // 创建服务端
  25. void _newLocalServer();
  26. // 激活窗口
  27. void _activateWindow();
  28.  
  29. bool _isRunning; // 是否已經有实例在运行
  30. QLocalServer *_localServer; // 本地socket Server
  31. QString _serverName; // 服务名称
  32. };
  33.  
  34. #endif // SINGLEAPPLICATION_H

CPP文件:

  1. #include "SingleApplication.h"
  2. #include <QtNetwork/QLocalSocket>
  3. #include <QFileInfo>
  4.  
  5. #define TIME_OUT (500) // 500ms
  6.  
  7. SingleApplication::SingleApplication(int &argc, char **argv)
  8. : QApplication(argc, argv)
  9. , w(NULL)
  10. , _isRunning(false)
  11. , _localServer(NULL) {
  12.  
  13. // 取应用程序名作为LocalServer的名字
  14. _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
  15.  
  16. _initLocalConnection();
  17. }
  18.  
  19. ////////////////////////////////////////////////////////////////////////////////
  20. // 说明:
  21. // 检查是否已經有一个实例在运行, true - 有实例运行, false - 没有实例运行
  22. ////////////////////////////////////////////////////////////////////////////////
  23. bool SingleApplication::isRunning() {
  24. return _isRunning;
  25. }
  26.  
  27. ////////////////////////////////////////////////////////////////////////////////
  28. // 说明:
  29. // 通过socket通讯实现程序单实例运行,监听到新的连接时触发该函数
  30. ////////////////////////////////////////////////////////////////////////////////
  31. void SingleApplication::_newLocalConnection() {
  32. QLocalSocket *socket = _localServer->nextPendingConnection();
  33. if(socket) {
  34. socket->waitForReadyRead(*TIME_OUT);
  35. delete socket;
  36.  
  37. // 其他处理,如:读取启动参数
  38.  
  39. _activateWindow();
  40. }
  41. }
  42.  
  43. ////////////////////////////////////////////////////////////////////////////////
  44. // 说明:
  45. // 通过socket通讯实现程序单实例运行,
  46. // 初始化本地连接,如果连接不上server,则创建,否则退出
  47. ////////////////////////////////////////////////////////////////////////////////
  48. void SingleApplication::_initLocalConnection() {
  49. _isRunning = false;
  50.  
  51. QLocalSocket socket;
  52. socket.connectToServer(_serverName);
  53. if(socket.waitForConnected(TIME_OUT)) {
  54. fprintf(stderr, "%s already running.\n",
  55. _serverName.toLocal8Bit().constData());
  56. _isRunning = true;
  57. // 其他处理,如:将启动参数发送到服务端
  58. return;
  59. }
  60.  
  61. //连接不上服务器,就创建一个
  62. _newLocalServer();
  63. }
  64.  
  65. ////////////////////////////////////////////////////////////////////////////////
  66. // 说明:
  67. // 创建LocalServer
  68. ////////////////////////////////////////////////////////////////////////////////
  69. void SingleApplication::_newLocalServer() {
  70. _localServer = new QLocalServer(this);
  71. connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
  72. if(!_localServer->listen(_serverName)) {
  73. // 此时监听失败,可能是程序崩溃时,残留进程服务导致的,移除之
  74. if(_localServer->serverError() == QAbstractSocket::AddressInUseError) {
  75. QLocalServer::removeServer(_serverName); // <-- 重点
  76. _localServer->listen(_serverName); // 再次监听
  77. }
  78. }
  79. }
  80.  
  81. ////////////////////////////////////////////////////////////////////////////////
  82. // 说明:
  83. // 激活主窗口
  84. ////////////////////////////////////////////////////////////////////////////////
  85. void SingleApplication::_activateWindow() {
  86. if(w) {
  87. w->show();
  88. w->raise();
  89. w->activateWindow(); // 激活窗口
  90. }
  91. }

调用示例:

  1. #include "MainWindow.h"
  2. #include "SingleApplication.h"
  3.  
  4. int main(int argc, char *argv[]) {
  5. SingleApplication a(argc, argv);
  6. if(!a.isRunning()) {
  7. MainWindow w;
  8. a.w = &w;
  9.  
  10. w.show();
  11.  
  12. return a.exec();
  13. }
  14. return ;
  15. }

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. Qt之QHeaderView自定义排序(QSortFilterProxyModel)

    简述 对以上节的排序,我们衍伸了两点: 把一个字符串前面的数据按照字符串比较,而后面的数据按照整形比较. 将整形显示为字符串,而排序依然正常呢. 为了分别描述,这里我们先解决问题1. 简述 效果 处理 ...

  2. jsp内置对象和el表达式

    九个内置对象 *out jsp的输出流,用来向客户端响应 *page 当前jsp页面, 它的引用数据类型是Object,即真身中有如下代码 Object page=this; *config 它对应真 ...

  3. css的使用技巧资料

    http://www.instantshift.com/2010/03/15/47-css-tips-tricks-to-take-your-site-to-the-next-level/ http: ...

  4. linux中备份mysql数据库的一个shell脚本

    #!/bin/bash #FileName:select_into_bak.sh #Desc:Use select into outfile to backup db or tables #Creat ...

  5. spring整合各大ORM框架的原理图

  6. linux 命令部分说明

    shell 文件头格式   #! /bin/sh 定义变量  dir_tmp=/tmp/xxx 级联创建 mkdir -p /etc/aaa/bbb 阻塞命令 等待用户输入回车  继续    read ...

  7. Oracle 中的 TO_DATE 和 TO_CHAR 函数

    Oracle 中的 TO_DATE 和 TO_CHAR 函数oracle 中 TO_DATE 函数的时间格式,以 2008-09-10 23:45:56 为例 格式 说明 显示值 备注 Year(年) ...

  8. 【转】gcc中-pthread和-lpthread的区别

    原文网址:http://chaoslawful.iteye.com/blog/568602 用gcc编译使用了POSIX thread的程序时通常需要加额外的选项,以便使用thread-safe的库及 ...

  9. ArcEngine9.3报错Create output feature class failed

    ArcEngine9.3执行IFeatureDataConverter.ConvertFeatureClass Method出错如下错误信息: Create output feature class ...

  10. 输出图片的php代码前面不能有空白行

    第一行增加一个空白行,就至少会输出一个换行符在图片数据流的前面而图片是按图片流提供的信息显示的,前面多了内容就无法解析了.