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

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

Комментарии

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

C++ - Тест 006. Перечисления

  • Результат:10баллов,
  • Очки рейтинга-10
K
  • KiRi4
  • 7 сентября 2023 г. 17:57

C++ - Тест 002. Константы

  • Результат:41баллов,
  • Очки рейтинга-8
K
  • KiRi4
  • 7 сентября 2023 г. 17:49

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

  • Результат:66баллов,
  • Очки рейтинга-1
Последние комментарии
IscanderChe
IscanderChe13 сентября 2023 г. 19:11
Пример использования QScintilla C++ По горячим следам (с другого форума вопрос задали, пришлось в памяти освежить всё) решил дополнить. Качаем исходники с https://riverbankcomputing.com/software/qscintilla/downlo…
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 17:18
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?" Могут. Выполняя оператор new , Вы выделяете под объект память в куче (heap), …
AC
Andrei Cherniaev5 сентября 2023 г. 13:37
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Я поясню свой вопрос. Выше я писал "Почему же в методе MainWindow::on_write_1_clicked() Можно обращаться к методам exampleObject_1? Разве могут взаимодействовать объекты из разных…
n
nvn31 августа 2023 г. 19:47
QML - Урок 004. Сигналы и слоты в Qt QML Здравствуйте! Прекрасный сайт, отличные статьи. Не хватает только готовых проектов для скачивания. Многих комментариев типа appCore != AppCore просто бы не было )))
NSProject
NSProject24 августа 2023 г. 23:40
Django - Урок 023. Like Dislike система с помощью GenericForeignKey Ваша ошибка связана с gettext from django.utils.translation import gettext_lazy as _ Поле должно выглядеть так vote = models.SmallIntegerField(verbose_name=_("Голос"), choices=VOTES) …
Сейчас обсуждают на форуме
IscanderChe
IscanderChe17 сентября 2023 г. 19:24
Интернационализация строк в QMessageBox Странная картина... Сделал минимально работающий пример - всё работает. Попробую на другой операционке. Может, дело в этом.
NSProject
NSProject17 сентября 2023 г. 18:49
Помогите добавить Ajax в проект В принципе ничего сложного с отправкой на сервер нет. Всё что ты хочешь отобразить на странице передаётся в шаблон и рендерится. Ты просто создаёшь файл forms.py в нём описываешь свою форму и в …
BlinCT
BlinCT15 сентября 2023 г. 22:35
Размеры полей в TreeView Всем привет. Пытаюсь сделать дерево вот такого вида Пытаюсь организовать делегат для каждой строки в дереве. ТО есть отступ какого то размера и если при открытии есть под…
IscanderChe
IscanderChe8 сентября 2023 г. 22:07
Кастомная QAbstractListModel и цвет фона, цвет текста и шрифт Похоже надо не абстрактный , а "реальный" типа QSqlTableModel Да, но не совсем. Решилось с помощью стайлшитов и setFont. Спасибо за отлик!
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 16:35
Вопрос: Нужно ли в деструкторе удалять динамически созданные QT-объекты. Напр: Зависит от того, как эти объекты были созданы. Если вы передаёте указатель на parent объект, то не нужно, Ядро Qt само разрулит удаление, если нет, то нужно удалять вручную, иначе будет ут…

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