Evgenii Legotckoi
Evgenii Legotckoi28 января 2016 г. 11:11

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

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

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

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

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

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

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

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

#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. При поднятии семафора, все другие экземпляры приложения уже не имеют доступа к разделяемой памяти и соответственно один экземпляр полностью владеет ресурсами. Данный экземпляр проверяет наличие запущенного другого экземпляра приложения по наличию сегмента разделяемой памяти с идентификатором, соответствующим данному приложению. Экземпляр успешно запускается и создаёт сегмент разделяемой памяти в случае, если не нашёл информации о другом экземпляре приложения. После этого семафор опускается, давая возможность другим экземплярам приложения попытаться запуститься.

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

#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 хостинг.

Вам это нравится? Поделитесь в социальных сетях!

IscanderChe
  • 25 ноября 2018 г. 3:49

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

Evgenii Legotckoi
  • 25 ноября 2018 г. 12:20

Добрый день!

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

IscanderChe
  • 25 ноября 2018 г. 13:24

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 8:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 8:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 19:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 октября 2024 г. 21:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 15:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 14:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 18:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
Evgenii Legotckoi
Evgenii Legotckoi24 июня 2024 г. 22:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 14:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 10:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 октября 2024 г. 16:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Следите за нами в социальных сетях