Das Ausführen nur einer Anwendungsinstanz kann erforderlich sein, um Probleme mit Speicherlecks zu begrenzen oder um mögliche Probleme mit zwei Anwendungsinstanzen zu beseitigen, die um dieselben Ressourcen, Dateien, SQLite-Datenbank usw. konkurrieren. Oder wenn die Anwendung im Prinzip nur eine vom Benutzer verwendete Instanz annimmt.
Um dieses Problem zu lösen, können Sie die folgenden zwei Methoden verwenden:
- Verwenden von QLockFile - wenn eine temporäre Datei erstellt wird, die beim Schließen der Anwendung zerstört wird. Gleichzeitig wird beim Start der zweiten Instanz der Anwendung geprüft, ob diese Datei existiert, und wenn die Datei bereits von einer geöffneten Instanz der Anwendung erstellt wurde, wird die zweite Instanz automatisch geschlossen;
- Verwendung von QSystemSemaphore und QSharedMemory - in diesem Fall wird ein gemeinsam genutztes Speichersegment erstellt und versucht, es durch eine eindeutige Kennung an ein vorhandenes Segment anzuhängen. Wenn der Verbindungsversuch erfolgreich ist, wurde bereits eine Anwendungsinstanz erstellt. Dementsprechend weisen wir den Nutzer darauf hin und schließen die Anwendung. Wenn der Verbindungsversuch nicht erfolgreich war, erstellen wir ein Speichersegment für die Anwendung und starten die erste Instanz.
Alle weiteren Programmcodes gehen nicht über main.cpp hinaus.
Einzelne Anwendung mit QLockFile
Im folgenden Code wird eine Sperrdatei im Ordner für temporäre Dateien erstellt, wenn die Anwendung gestartet wird;
Notiz. Ersetzen
#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(); }
Einzelanwendung mit QSystemSemaphore und QSharedMemory
Die erste Option bietet eine einfache und bequeme Lösung für das Problem, die Anzahl der ausgeführten Instanzen einer Qt -Anwendung zu begrenzen. Die QLockFile -Lösung hat jedoch auch Nachteile, da es möglicherweise zu Berechtigungsproblemen für den Benutzer kommt. Wenn Sie das Programm auf die Ausführung in einer Instanz auf dem gesamten Computer beschränken möchten, unabhängig davon, wie viele Benutzersitzungen darauf ausgeführt werden können, bietet die Verwendung von QLockFile diese Funktion ebenfalls nicht.
QSharedMemory hingegen wird von allen Benutzern gemeinsam genutzt, die gleichzeitig am Computer arbeiten. Wenn also einer der Benutzer Ihr Programm gestartet hat, kann der zweite es nicht mehr ausführen.
Aber in diesem Fall sollten Sie die Unterschiede beim Arbeiten mit Shared Memory auf verschiedenen Plattformen nicht vergessen. Im Fall von Windows wird gemeinsam genutzter Speicher sowohl bei normaler Programmbeendigung als auch bei abnormaler Beendigung freigegeben. Bei Linux/Unix wird der Speicher beim Absturz nicht freigegeben.
Im folgenden Code wird ein Semaphor verwendet, um die Racebedingung aufzulösen, wenn mehrere Instanzen derselben Anwendung gleichzeitig ausgeführt werden. Das Semaphor wird mit einem Zähler erstellt, dessen maximale Anzahl 1 ist. Wenn das Semaphor ausgelöst wird, haben alle anderen Instanzen der Anwendung keinen Zugriff mehr auf den gemeinsamen Speicher und dementsprechend besitzt eine Instanz die Ressourcen vollständig. Diese Instanz sucht nach einer anderen Instanz der Anwendung, die ausgeführt wird, indem ein gemeinsames Speichersegment mit einer ID vorhanden ist, die mit der Anwendung übereinstimmt. Die Instanz startet erfolgreich und erstellt ein gemeinsames Speichersegment, falls sie keine Informationen zu einer anderen Anwendungsinstanz gefunden hat. Das Semaphor wird dann gelöscht, wodurch andere Instanzen der Anwendung versuchen können, zu starten.
Notiz. Ersetzen
#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, но я не уверен, как именно это делается.
Понятно. Спасибо!