背景
某些情况下,在Windows环境,用qt开发的程序想限制用户只能同时打开一个。
用 QLocalSocket 和 QLocalServer实现
思路:程序运行时,先创建一个socket,去连接指定名字的本地服务器。
1)如果连接上了,则说明已经存在一个app被启动了,则关闭此套接字。退出程序启动。
2)如果没连上,则创建一个指定名称的服务器。
代码:
SingleApplication.cpp
#include "SingleApplication.h"
#include <QFileInfo>
#include <QtNetwork/QLocalSocket>
/*
* QLocalSocket 和 QLocalServer 是在和本机通信时使用,无法跨主机。(本地进程间套接字通信)
*/
#define TIME_OUT 500 //500ms
SingleApplication::SingleApplication(int &argc, char **argv)
:QApplication(argc, argv)
{
m_server_name = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
InitConnect();
}
bool SingleApplication::IsRunning()
{
return m_is_running;
}
void SingleApplication::InitConnect()
{
m_is_running = false;
QLocalSocket socket;
socket.connectToServer(m_server_name);
bool ret = socket.waitForConnected(TIME_OUT);
if (ret) {
fprintf(stderr, "%s already running.\n",
m_server_name.toLocal8Bit().constData());
m_is_running = true;
/**其他处理,如 将启动参数发送到服务端**/
//!!!注释中带中文且用想/**/注释时,如果是只有一行,最好用/**xxx**/形式,因为由于编码问题,
//末尾的*/会被转码,导致程序运行不报错,但是实际上运行跟理想的顺序不一致
return;
}
//连接不上服务器,就创建一个
NewLocalServer();
}
void SingleApplication::NewLocalServer()
{
m_server = new QLocalServer(this);
connect(m_server, &QLocalServer::newConnection, this, &SingleApplication::NewLocalConnect);
if (!m_server->listen(m_server_name)) {
if (m_server->serverError() == QAbstractSocket::AddressInUseError) {
QLocalServer::removeServer(m_server_name);
m_server->listen(m_server_name);
}
}
}
void SingleApplication::NewLocalConnect()
{
QLocalSocket* socket = m_server->nextPendingConnection();
if (socket)
{
socket->waitForReadyRead(2 * TIME_OUT);
delete socket;
/*其他处理,如读取启动参数*/
ActiveMainWindows();
}
}
void SingleApplication::ActiveMainWindows()
{
if (m_widget)
{
m_widget->show();
m_widget->raise();
m_widget->activateWindow();// 激活窗口
}
}
SingleApplication .h
#pragma once
#include <QObject>
#include <QWidget>
#include <QApplication>
#include <QtNetwork/QLocalServer>
class SingleApplication : public QApplication
{
Q_OBJECT
public:
SingleApplication(int& argc, char** argv);
bool IsRunning();//是否已有运行的程序
private:
void InitConnect();
void NewLocalServer();
/**
* @brief NewLocalConnect 通过socket通讯实现程序单实例运行,监听到新的连接时触发该函数
*/
void NewLocalConnect();
/**
* @brief ActiveMainWindows 激活主窗口
*/
void ActiveMainWindows();
public:
QWidget *m_widget{nullptr};
private:
bool m_is_running{false};
QLocalServer *m_server{nullptr};
QString m_server_name{""};
};
main.cpp
SingleApplication app(argc, argv);
if (!app.IsRunning())
{
MainWindow w;
app.m_widget = &w;
w.show();
return app.exec();
}