Evgenii Legotckoi
Evgenii Legotckoi28 січня 2016 р. 10: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 р. 02:49

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

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

Добрый день!

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

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

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

Коментарі

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

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

  • Результат:21бали,
  • Рейтинг балів-10
АФ

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

  • Результат:30бали,
  • Рейтинг балів-10
ПЩ

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

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
P
Pisych27 лютого 2023 р. 04:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 січня 2024 р. 11:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 грудня 2023 р. 08:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 січня 2024 р. 04:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 грудня 2023 р. 06:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

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