QT运行单进程实例

单进程实例检测类(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();
相关推荐
Wyn_16 小时前
【ZMQ/QT】Windows11 + Qt 安装配置zmq(亲测可用)
qt·zmq·windows11
864记忆16 小时前
Qt创建连接注意事项
数据库·qt·nginx
赵民勇1 天前
Qt QML中Component模块详解
qt
不会c嘎嘎1 天前
QT中的常用控件 (三)
开发语言·qt
闫有尽意无琼1 天前
Qt局部变量“遮蔽(shadow)”成员变量导致lambda传参报错
开发语言·qt
寻找华年的锦瑟1 天前
Qt-YOLO-OpenCV
qt·opencv·yolo
南桥几晴秋1 天前
Qt显示类控件
开发语言·c++·qt
_OP_CHEN1 天前
【从零开始的Qt开发指南】(十八)Qt 事件进阶:定时器、事件分发器与事件过滤器的实战宝典
qt·前端开发·事件过滤器·qt事件·gui开发·qt定时器·事件分发器
晨风先生1 天前
打包Qt程序的脚本package.bat
开发语言·qt