Разрешение запуска только одного экземпляра приложения может быть необходимо для ограничения проблем с утечками памяти, либо для устранения возможных проблем с конкуренцией двух экземпляров приложения за одни ресурсы, файлы, база данных 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, но я не уверен, как именно это делается.
Понятно. Спасибо!