Дозвіл запуску лише одного екземпляра програми може бути необхідним для обмеження проблем з витоками пам'яті, або для усунення можливих проблем з конкуренцією двох екземплярів програми за ресурси, файли, база даних 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(); }
День добрый.
Подскажите, как с помощью этих примеров вместо вывода сообщения сделать активным окно запущенного экземпляра приложения?
Добрый день!
С помощью API целевой системы придётся найти нужный процесс и открыть окно. Но придётся писать платформозависимый код. В случае Windows нужно смотреть, как с помощью WinAPI искать процессы и пытаться открыть их окна, если они есть. В случае Linux там ещё сложнее, или через XLib, или через DBus, но я не уверен, как именно это делается.
Понятно. Спасибо!