Frage

Normalerweise kann ein Anwendungspaket auf OS X nur einmal gestartet werden, jedoch durch einfaches Kopieren des Bündels die gleiche Anwendung kann zweimal gestartet werden. Was ist die beste Strategie, um diese Möglichkeit zu erkennen und zu stoppen?

Unter Windows kann dieser Effekt nur durch die Anwendung erreicht werden, um eine benannte Ressource beim Start zu schaffen und dann beenden, wenn die benannte Ressource kann nicht erstellt werden, was darauf hinweist, dass ein anderer Prozess, der ausgeführt wird, hat bereits die gleiche Ressource erstellt. Diese Ressourcen werden in einer zuverlässigen Art und Weise unter Windows freigegeben, wenn die Anwendung beendet wird.

Das Problem, das ich gesehen habe bei der Recherche ist, dass die APIs auf OS X Zustand in dem Dateisystem halten und macht somit die für Fenster Strategie unzuverlässig, dh Dateien nach einem falschen Ausgang verweilenden fälschlicherweise angeben kann, dass die Anwendung bereits ausgeführt werden .

Die APIs ich verwenden können, um den gleichen Effekt auf OS X zu erreichen sind: Posix, Kohlenstoff und boost

.

Ideen?

War es hilfreich?

Lösung

A Low-Level-Lösung flock () verwenden.

Jede Instanz würde versuchen, eine Datei beim Start zu sperren, und wenn die Sperre fehlschlägt, dann wird eine andere Instanz bereits ausgeführt wird. Flocks werden automatisch freigegeben, wenn das Programm beendet wird, also keine Sorgen über veraltete Sperren.

Beachten Sie, dass, was auch immer Lösung Sie wählen, müssen Sie eine bewusste Entscheidung darüber, was es bedeutet, „mehrere Instanzen“ zu haben. Insbesondere dann, wenn mehrere Benutzer Ihre App zur gleichen Zeit ausgeführt wird, ist das in Ordnung?

Andere Tipps

Das ist extrem einfach in Snow Leopard:

- (void)deduplicateRunningInstances {
    if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]] count] > 1) {
        [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]] 
                         defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal];

        [NSApp terminate:nil];
    }
}

Siehe http: // blog .jseibert.com / post / 1167439217 / Deduplizierung-Lauf-Instanzen-oder-how-to-detect-wenn für weitere Informationen.

Es gibt einen mysteriösen Info.plist Schlüssel namens „Anwendung mehrere Instanzen verbietet“, aber es scheint nicht, für mich zu arbeiten. Ich bin eine CLI-Anwendung zu schreiben und es innerhalb eines Bündels ausgeführt wird. Vielleicht wäre es in einer GUI-Anwendung arbeiten, aber ich habe nicht versucht.

Wie bereits Cocoa-Anwendungen erwähnt in der Regel nicht erlaubt, mehr als eine Instanz zu einem Zeitpunkt ausgeführt werden.

Im Allgemeinen ist ein Kakao Weg, um diesen Blick auf launchedApplications in NSWorkspace zu lösen. Dies gibt einen NSArray ein Wörterbuch für jede gestartete Anwendung enthält. Sie können Schleife durch das Array zu sehen, ob die App Sie suchen, wird bereits ausgeführt. Ich würde empfehlen, dass Sie den Wert mit dem Schlüssel NSApplicationBundleIdentifier verwenden, die einen Wert wie „com.mycompany.myapp“ haben anstatt auf der Suche nach dem Namen. Wenn Sie den Bundle-Bezeichner für eine App finden möchten, können Sie an der info.plist Datei im App-Paket suchen.

Zunächst einmal, es ist „Mac OS X“ oder „OS X“. Es gibt nicht so etwas wie „O / X“.

Zweitens, Mac OS X kommt nicht mit Boost-; Sie würden es mit Ihrer Anwendung bündeln müssen.

Drittens, die meisten von Carbon ist nicht verfügbar in 64-Bit. Dies ist ein klares Signal, dass jene Abschnitte von Kohlenstoff weg irgendwann gehen werden (als Apple 32-Bit in seiner Hardware verzichtet). Früher oder später werden Sie müssen entweder neu schreiben Sie Ihre App mit Kakao oder Mac verlassen.

  

Normalerweise ein Anwendungspaket auf O / X kann nur einmal gestartet werden, jedoch einfach durch das Bündel der gleiche Anwendung Umbenennung kann zweimal gestartet werden.

Nein kann es nicht. die umbenannt oder verschoben Anwendung gestartet wird einfach aktivieren (bringen Sie nach vorne) dem Prozess, der bereits ausgeführt wurde; es wird nicht einen neuen, zweiten Prozess, der neben dem ersten beginnen.


Es gibt mehrere Möglichkeiten, zu sagen, ob eine Anwendung bereits ausgeführt wird. In jedem Fall tun Sie dies auf Start:

  1. Verwenden des Kakaos NSConnection eine Verbindung mit einem einzigen konstanten Namen zu registrieren. Dies wird fehlschlagen, wenn der Name bereits registriert ist. (Sie können Ihre Stiftung von einer Carbon-App verwenden,. Es ist das Application Kit Sie müssen vorsichtig sein mit)
  2. , um den Prozess-Manager Verwenden Sie die Prozessliste für Prozesse, deren Paket-ID übereinstimmen, die eine Sie suchen zu scannen. Der Bundle-Bezeichner ist nicht unveränderlich, aber es ist schwieriger als die Dateinamen oder Speicherort zu ändern.
  3. Wenn Sie schauen, um zu sehen, wenn jemand eine zweite Kopie von sich selbst läuft, können Sie CFNotificationCenter verwenden:

    1. Fügen Sie sich als Beobachter für „com.yourdomain.yourappname.LaunchResponse“.
    2. Senden Sie eine Benachrichtigung unter dem Namen „com.yourdomain.yourappname.LaunchCall“.
    3. Fügen Sie sich als Beobachter für „com.yourdomain.yourappname.LaunchCall“.

    In Ihrer Beobachtung Rückruf für die Anrufbenachrichtigung, veröffentlicht die Antwort Benachrichtigung.
    In Ihrer Beobachtung Rückruf für die Response-Benachrichtigung zu beenden.

    Wenn also der erste Prozess startet, wird es Anruf und keine Antwort erhalten; wenn der zweite Prozess beginnt, wird sie rufen, eine Antwort von dem ersten Prozess erhalten, und Ausfahrt aus Rücksicht auf den ersten.

Dies ist eine Kombination von Römer und Jeffs Antworten für Swift 2.0: Wenn eine andere Instanz der App mit dem gleichen Bündel ID bereits ausgeführt wird, eine Warnung anzuzeigen, aktivieren Sie die andere Instanz und die doppelte Instanz beenden

.
func applicationDidFinishLaunching(aNotification: NSNotification) {
    /* Check if another instance of this app is running. */
    let bundleID = NSBundle.mainBundle().bundleIdentifier!
    if NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID).count > 1 {
        /* Show alert. */
        let alert = NSAlert()
        alert.addButtonWithTitle("OK")
        let appName = NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleNameKey as String) as! String
        alert.messageText = "Another copy of \(appName) is already running."
        alert.informativeText = "This copy will now quit."
        alert.alertStyle = NSAlertStyle.CriticalAlertStyle
        alert.runModal()

        /* Activate the other instance and terminate this instance. */
        let apps = NSRunningApplication.runningApplicationsWithBundleIdentifier(bundleID)
        for app in apps {
            if app != NSRunningApplication.currentApplication() {
                app.activateWithOptions([.ActivateAllWindows, .ActivateIgnoringOtherApps])
                break
            }
        }
        NSApp.terminate(nil)
    }

    /* ... */
}

Was IPC ? Sie könnten einen Socket öffnen und zu verhandeln mit dem anderen ins Leben gerufen Instanz. Sie müßten allerdings vorsichtig sein, dass es funktioniert, wenn beide Anwendungen gleichzeitig starten.

Ich kann Ihnen nicht mit Beispielcode zur Verfügung stellen, wie ich (noch, aber ich werde bald) verwendet es.

Diese

ist eine Version von seb ist für Swift 3.0 . Wenn eine andere Instanz der App mit dem gleichen Bündel ID bereits ausgeführt wird, eine Warnung anzuzeigen, aktivieren Sie die andere Instanz, und beenden Sie die doppelte Instanz

func applicationDidFinishLaunching(aNotification: NSNotification) {
    /* Check if another instance of this app is running. */
    let bundleID = Bundle.main.bundleIdentifier!
    if NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count > 1 {
         /* Show alert. */
         let alert = NSAlert()
         alert.addButton(withTitle: "OK")
         let appName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String
         alert.messageText = "Another copy of \(appName) is already running."
         alert.informativeText = "This copy will now quit."
         alert.alertStyle = NSAlert.Style.critical
         alert.runModal()

         /* Activate the other instance and terminate this instance. */
         let apps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID)
             for app in apps {
                  if app != NSRunningApplication.current {
                      app.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
                      break
                  }
             }
                NSApp.terminate(nil)
         }   
       /* ... */
}

erkennen, wenn die Anwendung mit dem gleichen bund ausgeführt wird, aktivieren Sie es und schließen, was gestartet wird.

- (id)init method of < NSApplicationDelegate >

    NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
    if ([apps count] > 1)
    {
        NSRunningApplication *curApp = [NSRunningApplication currentApplication];
        for (NSRunningApplication *app in apps)
        {
            if(app != curApp)
            {
                [app activateWithOptions:NSApplicationActivateAllWindows|NSApplicationActivateIgnoringOtherApps];
                break;
            }
        }
        [NSApp terminate:nil];
        return nil;
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top