单进程实例检测类(Singleton)
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QSharedMemory>
#include <QSystemSemaphore>
class Singleton
{
public:
// 构造函数
explicit Singleton(const QString& key);
// 析构函数(必须实现)
~Singleton();
// 判断是否有其他实例运行
bool isAnotherRunning();
// 尝试运行当前实例
bool tryToRun();
// 释放资源
void release();
private:
QString m_key;
QSharedMemory m_sharedMem;
QSystemSemaphore m_semaphore;
};
#endif // SINGLETON_H
#include "singleton.h"
#include <QDebug>
Singleton::Singleton(const QString& key)
: m_key(key)
, m_sharedMem(key)
, m_semaphore(key + "_sem", 1, QSystemSemaphore::Create)
{
}
// 析构函数实现
Singleton::~Singleton()
{
release(); // 确保资源被释放
}
bool Singleton::isAnotherRunning()
{
if (m_sharedMem.attach()) {
m_sharedMem.detach();
return true; // 已经有一个实例在运行
}
return false;
}
bool Singleton::tryToRun()
{
m_semaphore.acquire();
bool isRunning = false;
if (m_sharedMem.attach()) {
isRunning = true; // 已存在实例
} else {
// 创建新的共享内存
if (!m_sharedMem.create(1)) {
qWarning() << "Failed to create shared memory:" << m_sharedMem.errorString();
m_semaphore.release();
return false;
}
isRunning = false; // 这是第一个实例
}
m_semaphore.release();
return !isRunning; // 返回true表示可以运行
}
void Singleton::release()
{
m_semaphore.acquire();
if (m_sharedMem.isAttached()) {
m_sharedMem.detach();
}
m_semaphore.release();
}
1、只运行一个实例,示例代码:
#include "singleton.h"
#include <QApplication>
#include <QMessageBox>
#include "mainwindow.h" // 你的主窗口头文件
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 创建单实例管理器
Singleton singleton("MyUniqueAppKey_v1.0");
// 尝试运行
if (!singleton.tryToRun()) {
// 已经有实例在运行
QMessageBox::information(nullptr, "提示",
"应用程序已经在运行中!\n"
"请检查任务栏或系统托盘。");
return 0; // 退出新实例
}
// 正常启动应用
MainWindow window;
window.show();
// 进入主事件循环
int ret = app.exec();
return ret;
}
2、只运行一个实例。存在进程时,激活。不存在时,启动。示例代码:
#include "singleton.h"
#include <QApplication>
#include <QMessageBox>
#ifdef Q_OS_WIN
#include <windows.h>
#include <tlhelp32.h>
// 通过窗口标题查找并激活
bool activateWindowByTitle(const QString &windowTitle)
{
HWND hwnd = FindWindowW(NULL, windowTitle.toStdWString().c_str());
if (hwnd == NULL) {
// 尝试通过类名查找
hwnd = FindWindowW(L"Qt5QWindowIcon", windowTitle.toStdWString().c_str());
if (hwnd == NULL) {
hwnd = FindWindowW(L"QWidget", NULL); // 尝试查找任何Qt窗口
}
}
if (hwnd != NULL) {
// 恢复窗口(如果最小化)
if (IsIconic(hwnd)) {
ShowWindow(hwnd, SW_RESTORE);
}
// 激活窗口
SetForegroundWindow(hwnd);
BringWindowToTop(hwnd);
// 可选:闪烁窗口
FlashWindow(hwnd, TRUE);
return true;
}
return false;
}
// 通过进程名查找并激活
bool activateWindowByProcessName(const QString &processName)
{
DWORD pid = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(snapshot, &processEntry)) {
do {
QString exeFile = QString::fromWCharArray(processEntry.szExeFile);
if (exeFile.compare(processName, Qt::CaseInsensitive) == 0) {
pid = processEntry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &processEntry));
}
CloseHandle(snapshot);
}
if (pid != 0) {
// 枚举该进程的所有窗口
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
DWORD windowPid;
GetWindowThreadProcessId(hwnd, &windowPid);
if (windowPid == (DWORD)lParam) {
// 找到属于该进程的窗口
if (IsWindowVisible(hwnd)) {
// 恢复窗口
if (IsIconic(hwnd)) {
ShowWindow(hwnd, SW_RESTORE);
}
// 激活窗口
SetForegroundWindow(hwnd);
BringWindowToTop(hwnd);
// 停止枚举
return FALSE;
}
}
return TRUE; // 继续枚举
}, (LPARAM)pid);
return true;
}
return false;
}
#endif
#include "mainwindow.h" // 你的主窗口头文件
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//获取appName
QString exePath = QCoreApplication::applicationFilePath();
QFileInfo fileInfo(exePath);
QString appName = fileInfo.fileName();
// 创建单实例管理器
Singleton singleton("MyUniqueAppKey_v1.0");
// 尝试运行
if (!singleton.tryToRun()) {
// 已经有实例在运行
activateWindowByProcessName(appName);
return 0; // 退出新实例
}
// 正常启动应用
MainWindow window;
window.show();
// 进入主事件循环
int ret = app.exec();
return ret;
}
3、只运行一个实例。但本实例自动重启。示例代码:
#include "singleton.h"
#include <QApplication>
#include <QMessageBox>
#ifdef Q_OS_WIN
#include <windows.h>
#include <tlhelp32.h>
// 通过进程名查找并激活
bool activateWindowByProcessName(const QString &processName)
{
DWORD pid = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(snapshot, &processEntry)) {
do {
QString exeFile = QString::fromWCharArray(processEntry.szExeFile);
if (exeFile.compare(processName, Qt::CaseInsensitive) == 0) {
pid = processEntry.th32ProcessID;
break;
}
} while (Process32NextW(snapshot, &processEntry));
}
CloseHandle(snapshot);
}
if (pid != 0) {
// 枚举该进程的所有窗口
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
DWORD windowPid;
GetWindowThreadProcessId(hwnd, &windowPid);
if (windowPid == (DWORD)lParam) {
// 找到属于该进程的窗口
if (IsWindowVisible(hwnd)) {
// 恢复窗口
if (IsIconic(hwnd)) {
ShowWindow(hwnd, SW_RESTORE);
}
// 激活窗口
SetForegroundWindow(hwnd);
BringWindowToTop(hwnd);
// 停止枚举
return FALSE;
}
}
return TRUE; // 继续枚举
}, (LPARAM)pid);
return true;
}
return false;
}
#endif
#include "mainwindow.h" // 你的主窗口头文件
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 检查是否带有重启标志(本实例界面手工重启程序)
bool isRestart = false;
for (int i = 0; i < argc; ++i) {
if (QString(argv[i]) == "--restart") {
isRestart = true;
break;
}
}
//获取appName
QString exePath = QCoreApplication::applicationFilePath();
QFileInfo fileInfo(exePath);
QString appName = fileInfo.fileName();
// 创建单实例管理器
Singleton singleton("MyUniqueAppKey_v1.0");
if(!isRestart){
//尝试运行
if (!singleton.tryToRun()) {
// 已经有一个实例在运行
activateWindowByProcessName(appName);
return 0;
}
}
// 正常启动应用
MainWindow window;
window.show();
// 进入主事件循环
int ret = app.exec();
return ret;
}
手工重启代码:
// 获取当前程序的路径和参数
QString program = QApplication::applicationFilePath();
QStringList arguments = QApplication::arguments();
arguments << "--restart"; // 添加重启标志参数
// 启动新进程(startDetached 会在当前程序退出后运行)
QProcess::startDetached(program, arguments);
// 关闭当前程序
QApplication::quit();