Евгений Легоцкой28 січня 2016 р. 11:11

Qt/C++ - Урок 043. Qt Single Application - запускаем только один экземпляр приложения

Разрешение запуска только одного экземпляра приложения может быть необходимо для ограничения проблем с утечками памяти, либо для устранения возможных проблем с конкуренцией двух экземпляров приложения за одни ресурсы, файлы, база данных SQLite и т.д. Либо если приложение в принципе предполагает только один экземпляр, используемый пользователем.

Для решения данной задачи можно использовать два следующих способа:

  • С использованием QLockFile - когда создаётся временный файл, который уничтожается при закрытии приложения. При этом при запуске второго экземпляра приложения происходит проверка на существование данного файла и если файл уже создан одним открытым экземпляром приложения, то второй экземпляр автоматически закрывается;
  • С использованием QSystemSemaphore и QSharedMemory - в данном случае создаётся сегмент разделяемой памяти и производится попытка присоединить его к существующему сегменту по уникальному идентификатору. Если попытка присоединения прошла успешно, значит один экземпляр приложения уже создан. Соответственно, сообщаем об этом пользователю и закрываем приложение. В случае, если попытка присоединения прошла безуспешно, то создаём выделяем сегмент памяти для приложения и запускаем первый экземпляр.

Весь дальнейший программный код не будет выходить за пределы main.cpp.

Single application с использованием QLockFile

В представленном ниже коде в папке для временных файлов при запуске приложения создаётся Lock File , в случае неудачной попытки создания Lock File , программа считает, что уже открыт один экземпляр приложения, сообщает об этом пользователю и закрывается.

Примечание. Замените <uniq id> на ваш идентификатор.

#include "widget.h"
#include <QApplication>
#include <QLockFile>
#include <QDir>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QLockFile lockFile(QDir::temp().absoluteFilePath("lurity.lock"));

    /* Пытаемся закрыть Lock File, если попытка безуспешна в течение 100 миллисекунд,
     * значит уже существует Lock File созданный другим процессом.
     * Следовательно, выбрасываем предупреждение и закрываем программу
     * */
    if(!lockFile.tryLock(100)){
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setText("Приложение уже запущено.\n"
                       "Разрешено запускать только один экземпляр приложения.");
        msgBox.exec();
        return 1;
    }

    Widget w;
    w.show();

    return a.exec();
}

Single application с использованием QSystemSemaphore и QSharedMemory

В первом варианте дано простое и удобное решение задачи по ограничению количества запущенных экземпляров Qt приложения. Но решение на QLockFile имеет и недостатки в том плане, что могут быть проблемы с разрешениями для пользователя. А также, если вы желаете ограничить, запуск программы одним экземпляром на весь компьютер, не различия то, сколько пользовательских сессий может быть запущено на нём, то использование QLockFile также не обеспечит эту возможность.

QSharedMemory же напротив является разделяемой для всех пользователей одновременно, работающих за компьютером. Поэтому, если один из пользователей запустил Вашу программу, то второй уже не сможет её запустить.

Но в данном варианте необходимо не забывать о различиях в работе с разделяемой памятью под различными платформами. В случае с Windows, разделяемая память будет освобождена как при штатном завершении программы, так и при аварийном завершении. В случае же с Linux/Unix при аварийном при аварийном завершении память освобождена не будет.

В ниже представленном коде семафор используется для разрешения проблемы гонок в случае с одновременным запуском нескольких экземпляров одного приложения. Семафор создаётся со счётчиком, максимальное число которого равно 1. При поднятии семафора, все другие экземпляры приложения уже не имеют доступа к разделяемой памяти и соответственно один экземпляр полностью владеет ресурсами. Данный экземпляр проверяет наличие запущенного другого экземпляра приложения по наличию сегмента разделяемой памяти с идентификатором, соответствующим данному приложению. Экземпляр успешно запускается и создаёт сегмент разделяемой памяти в случае, если не нашёл информации о другом экземпляре приложения. После этого семафор опускается, давая возможность другим экземплярам приложения попытаться запуститься.

Примечание. Замените <uniq id> и <uniq id 2> на ваши идентификатор.

#include "mainwindow.h"
#include <QApplication>
#include <QSystemSemaphore>
#include <QSharedMemory>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QSystemSemaphore semaphore("<uniq id>", 1);  // создаём семафор
    semaphore.acquire(); // Поднимаем семафор, запрещая другим экземплярам работать с разделяемой памятью

#ifndef Q_OS_WIN32
    // в linux/unix разделяемая память не освобождается при аварийном завершении приложения,
    // поэтому необходимо избавиться от данного мусора
    QSharedMemory nix_fix_shared_memory("<uniq id 2>");
    if(nix_fix_shared_memory.attach()){
        nix_fix_shared_memory.detach();
    }
#endif

    QSharedMemory sharedMemory("<uniq id 2>");  // Создаём экземпляр разделяемой памяти
    bool is_running;            // переменную для проверки ууже запущенного приложения
    if (sharedMemory.attach()){ // пытаемся присоединить экземпляр разделяемой памяти
                                // к уже существующему сегменту
        is_running = true;      // Если успешно, то определяем, что уже есть запущенный экземпляр
    }else{
        sharedMemory.create(1); // В противном случае выделяем 1 байт памяти
        is_running = false;     // И определяем, что других экземпляров не запущено
    }
    semaphore.release();        // Опускаем семафор

    // Если уже запущен один экземпляр приложения, то сообщаем об этом пользователю
    // и завершаем работу текущего экземпляра приложения
    if(is_running){
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setText(QObject::trUtf8("Приложение уже запущено.\n"
                        "Вы можете запустить только один экземпляр приложения."));
        msgBox.exec();
        return 1;
    }

    MainWindow w;
    w.show();

    return a.exec();
}

Видеоурок

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Підтримайте автора Donate

День добрый.
Подскажите, как с помощью этих примеров вместо вывода сообщения сделать активным окно запущенного экземпляра приложения?

Добрый день!

С помощью API целевой системы придётся найти нужный процесс и открыть окно. Но придётся писать платформозависимый код. В случае Windows нужно смотреть, как с помощью WinAPI искать процессы и пытаться открыть их окна, если они есть. В случае Linux там ещё сложнее, или через XLib, или через DBus, но я не уверен, как именно это делается.

Понятно. Спасибо!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Timeweb

Дозвольте порекомендувати вам чудовий хостинг, на якому розташований EVILEG.

Протягом багатьох років Timeweb доводив свою стабільність.

Для проектів на Django рекомендую VDS хостинг

Переглянути хостинг
VD

C++ - Тест 001. Первая программа и типы данных

  • Результат:73бали,
  • Рейтинг балів1
Ds

C++ - Тест 003. Условия и циклы

  • Результат:64бали,
  • Рейтинг балів-1
o
  • ost.vld
  • 26 липня 2020 р. 05:46

C++ - Тест 001. Первая программа и типы данных

  • Результат:86бали,
  • Рейтинг балів6
Останні коментарі
s

Django - Урок 023. Like Dislike система с помощью GenericForeignKey

все, я со всем разобрался!) Извините!)
s

Django - Урок 023. Like Dislike система с помощью GenericForeignKey

Доброго времени суток!) Я случайно набрел на вашу статью, и она помогла мне решить некоторые мои трудности, я прошел за вами по шагам, в попытках адаптировать это под себя, и возник вопрос. У ва…
R

Qt/C++ - Урок 026. Применение CallBack функции

Спасибо огромное!

Qt/C++ - Урок 026. Применение CallBack функции

Если не объявлять статической, то не соберётся. Не получится сделать привязку метода. Дело в том, что в процессе компиляции производится сборка с указанием конкретных участков кода в данном…
  • Anton
  • 04 серпня 2020 р. 02:25

Django - Урок 039. Добавление личных сообщений и чатов на сайте - Часть 2 (Счётчик диалогов и чатов с непрочитанными сообщениями)

Может быть посоветуете как добавить необязательное поле + прокинуть его во вьюху что бы можно было отправлять небольшие документы.?
Тепер обговоріть на форумі
s

Ключевое слово class

Если закоментить include , то ошибка будет такая : C2027(использование неопределённого типа "Autorization"). Если убрать ключевое слово class , то ошибки будет 3 : Ошибка C2143 син…

Как в Android открыть свернутое приложение?

Может быть поможет это

Динамическое изменение цветовой схемы material

как по мне, темы типа material хороши если вас полностью устраивает их стил, если пишете кастомный дизайн то имеет смысл отказаться

QT QGraphics преобразование координат

Добрый день. Да, можно перевести в координаты экрана. Используйте метод mapToGlobal() . В Qt документации на класс приводятся методы, которые внедрены в конкретном классе. В нач…
М

QML связь с моделью в C++

Спасибо.
Про
Послуги
© EVILEG 2015-2020
Рекомендуємо хостинг TIMEWEB