欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

QT程序实现单例启动

程序员文章站 2024-01-03 13:55:04
...

现实应用中在很多场合下,要求程序只能启动一个实例。比如,Windows PC端只允许启动一个任务管理器。

  • 目前使QT运行一个实例有以下几种方式:

  • 1.QSharedMemory。

    使用共享内存,当第二个进程启动时,判断内存区数据是否建立,如有,则退出; 这种方式有弊端,在程序发生崩溃时,未及时清除共享区数据,导致程序不能正常启动.

  • 2.QtSingleApplication。

  • 使用QT扩展库QtSingleApplication,能很好的解决这个问题. 详见: http://qt.nokia.com/products/qt-addons/solutions-archive.
    但是QtSingleApplication并非QT官方自带的,需要额外自行下载。

  • 3.利用QLocalServer实现。

    本文将示范这种用法。

  • 4.利用文件锁。

在程序运行的时候就在目录下创建一个文件,当程序运行时就判断这个文件是否存在,如果存在说明程序已经在运行。其本质与QSharedMemory相同。在程序发生崩溃时,未及时清除文件,导致程序不能正常启动。

======================================================================================================
本机环境 Win10 + vs2015 + qt 5.6.12.

利用QLocalServer实现单例启动的原理很简单。

当第1次启动程序时,在MyApplication类的构造函数中会尝试以本地客户端去连接一个标记为serverName的localServer。因为此时该localServer并未创建,并不存在,所以连接失败(Can’t connect to server)。于是,新建一个server(build a server),并绑定监听serverName( m_server->listen(serverName) )。此时依然保持有m_isRunning = false,故程序可以启动:

	if (app.isRunning())//返回的是m_isRunning = false
	{
		QMessageBox::critical(nullptr, QString("错误"), "检测到程序已经启动!");
		return 0;
	}

当第2次启动程序时,标记为serverName的localServer已创建。同样地,在MyApplication类的构造函数中会尝试以本地客户端去连接一个标记为serverName的localServer。显然,连接成功(执行m_isRunning = true),随后直接完成MyApplication的构造函数。
所以在main函数中可以进行单例判定,阻止程序再次启动:

	if (app.isRunning())//返回的是m_isRunning = true
	{
		QMessageBox::critical(nullptr, QString("错误"), "检测到程序已经启动!");
		return 0;
	}

新建类MyApplication继承于QApplication。
MyApplication.h

#pragma once
#include <QtWidgets/QApplication>
#include <QtNetwork/QLocalServer>

class MyApplication : public QApplication
{
	Q_OBJECT
public:
	MyApplication(int argc, char **argv);

	bool isRunning();

	private slots:
	void newLocalConnection();

private:
	QLocalServer *m_server;

	bool m_isRunning;
};

MyApplication.cpp

#include "MyApplication.h"
#include <QtNetwork/QLocalSocket>
#include <QFile>

MyApplication::MyApplication(int argc, char **argv) :QApplication(argc, argv)
{
	m_isRunning = false;

	QCoreApplication::setApplicationName(QCoreApplication::applicationName()+"-localserver");
	QString serverName = QCoreApplication::applicationName();

	QLocalSocket socket;
	socket.connectToServer(serverName);

	if (socket.waitForConnected(500))
	{
		QTextStream stream(&socket);
		QStringList args = QCoreApplication::arguments();

		if (args.count()>1)
		{
			stream << args.last();
		}
		else
		{
			stream << QString();
		}
		stream.flush();
		//qDebug() << "Connected server,program will quit";

		socket.waitForBytesWritten();
		/**
		*qApp->quit(); 此代码是用来退出事件循环的;在构造函数中,事件循环
		*尚未启动,因此就无法退出. 最好就是设置标志判断在外部判断
		*/
		m_isRunning = true;

		return;
	}

	//qDebug() << "Can't connect to server,build a server";
	m_server = new QLocalServer(this);
	connect(m_server, SIGNAL(newConnection()), this, SLOT(newLocalConnection()));

	if (m_server->listen(serverName))
	{
		//防止程序崩溃时,残留进程服务,移除它
		if (m_server->serverError() == QAbstractSocket::AddressInUseError&&QFile::exists(m_server->serverName()))
		{
			QFile::remove(m_server->serverName());
			m_server->listen(serverName);
		}
	}
}


//槽函数
void MyApplication::newLocalConnection()
{
	QLocalSocket *socket = m_server->nextPendingConnection();
	if (!socket)
		return;

	socket->waitForReadyRead(1000);

	////显示传入参数值
	//QTextStream in(socket);
	//QString vl;
	//in >> vl;
	////qDebug() << "The value is: " << vl;

	delete socket;
}


bool MyApplication::isRunning()
{
	return m_isRunning;
}

在main.cpp中使用的方式

int main(int argc, char *argv[])
{
	MyApplication app(argc, argv);
	if (app.isRunning())//单例启动
	{
		QMessageBox::critical(nullptr, QString("错误"), "检测到程序已经启动!");
		return 0;
	}
		
	MyUI w;
	w.show();
	return app.exec();
}
相关标签: 单例启动 qt

上一篇:

下一篇: