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();
相关推荐
Larry_Yanan4 小时前
Qt网络开发之基于 QWebEngine 实现简易内嵌浏览器
linux·开发语言·网络·c++·笔记·qt·学习
一然明月6 小时前
Qt QML 锚定(Anchors)全解析
java·数据库·qt
一只爱学习的小鱼儿6 小时前
使用QT编写粒子显示热力图效果
开发语言·qt
大树学长6 小时前
【QT开发】Redis通信相关(一)
redis·qt
笨笨马甲6 小时前
Qt 人脸识别
开发语言·qt
山上三树7 小时前
Qt QObject介绍
开发语言·qt
山上三树7 小时前
QObject、QWidget、Widget三者的关系
qt
坚定学代码8 小时前
qt c++ 局域网聊天小工具
c++·qt·个人开发
笨笨马甲9 小时前
Qt network开发
开发语言·qt
mengzhi啊1 天前
Qt Designer UI 界面 拖的两个 QLineEdit,想按 Tab 从第一个跳到第二个
qt