Evgenii Legotckoi
Evgenii Legotckoi28 січня 2016 р. 11:11

Qt/C++ - Урок 043. Одна програма Qt - Запуск лише одного екземпляра програми

Дозвіл запуску лише одного екземпляра програми може бути необхідним для обмеження проблем з витоками пам'яті, або для усунення можливих проблем з конкуренцією двох екземплярів програми за ресурси, файли, база даних 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 р. 03:49

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

Evgenii Legotckoi
  • 25 листопада 2018 р. 12:20

Добрый день!

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

IscanderChe
  • 25 листопада 2018 р. 13:24

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

Коментарі

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

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

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

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

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

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

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

Слідкуйте за нами в соціальних мережах