Pregunta

Normalmente, un paquete de aplicaciones en OS X sólo se puede iniciar una vez, sin embargo, simplemente copiando el paquete de la misma aplicación se puede ejecutar dos veces. ¿Cuál es la mejor estrategia para detectar y detener esta posibilidad?

En Windows este efecto puede simplemente puede lograr mediante la aplicación de la creación de un recurso con nombre en el lanzamiento y luego la salida si el recurso con nombre no puede ser creado, lo que indica que se está ejecutando otro proceso que ya se ha creado el mismo recurso. Estos recursos se liberan de una manera fiable en Windows cuando la aplicación se cierra.

El problema que he visto cuando la investigación de esto es que las API de OS X mantener el estado del sistema de archivos y por lo tanto hace que la estrategia utilizada en las ventanas no fiable, es decir persistentes archivos después de una salida incorrecta falsamente puede indicar que la aplicación ya está en ejecución .

Las API que pueda usar para conseguir el mismo efecto en OS X son: POSIX, el carbono y el impulso

.

Las ideas?

¿Fue útil?

Solución

Una solución de bajo nivel es utilizar flock ().

Cada instancia intentaría bloquear un archivo en el arranque, y si falla el bloqueo luego otra instancia ya está en marcha. Los rebaños se liberan automágicamente cuando el programa sale, por lo que no se preocupa por viejos bloqueos.

Tenga en cuenta que cualquiera que sea la solución que elija, es necesario tomar una decisión consciente sobre lo que significa tener "múltiples instancias". En concreto, si varios usuarios están ejecutando la aplicación, al mismo tiempo, es esto correcto?

Otros consejos

Esto es extremadamente fácil en 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];
    }
}

http: // Blog .jseibert.com / post / 1167439217 / para más información.-casos-o-how de detectar, si la deduplicación de duración

Hay una llave Info.plist misteriosa llamada "Aplicación prohíbe varias instancias," pero no parece funcionar para mí. Estoy escribiendo una aplicación CLI y ejecutarlo desde dentro de un paquete. Tal vez sería trabajar en una aplicación de interfaz gráfica de usuario, pero no he probado.

Como ya se ha mencionado las aplicaciones Cocoa por lo general no le permiten ejecutar más de una instancia a la vez.

En general, una manera de cacao para resolver este vistazo a launchedApplications en NSWorkspace. Esto devuelve un NSArray que contiene un diccionario para cada aplicación lanzada. Puede recorrer la matriz para ver si la aplicación que busca ya se está ejecutando. Yo aconsejaría que use el valor con la tecla NSApplicationBundleIdentifier que tendrá un valor como "com.mycompany.myapp" en lugar de buscar el nombre. Si usted necesita encontrar el identificador de paquete para una aplicación se puede ver en su archivo info.plist en el paquete de la aplicación.

En primer lugar, se trata de “Mac OS X” o “OS X”. No hay tal cosa como “OS / X”.

En segundo lugar, el Mac OS X no viene con Boost; lo que se necesita para agrupar con su solicitud.

En tercer lugar, la mayor parte de carbono no está disponible en 64 bits. Esta es una clara señal de que aquellas partes de carbono van a desaparecer algún día (cuando Apple abandona 32 bits en su hardware). Tarde o temprano, tendrá que volver a escribir bien su aplicación con cacao o abandonar el Mac.

  

Normalmente, un paquete de aplicaciones en OS / X sólo puede iniciarse una vez, sin embargo, cambiando simplemente el nombre del paquete de la misma aplicación se puede ejecutar en dos ocasiones.

No, no puede. El lanzamiento de la aplicación renombrado o movido simplemente activar (poner al frente) el proceso que ya estaba en funcionamiento; no se iniciará un nuevo segundo proceso, junto con la primera.


Hay varias maneras de saber si una aplicación se está ejecutando. En todos los casos, esto se hace en el lanzamiento:

  1. Uso NSConnection de cacao a registrar una conexión con un solo nombre constante. Esto se producirá un error si el nombre ya está registrado. (Puede utilizar una aplicación Fundación de carbono;. Que es el Kit de aplicaciones que tienes que tener cuidado con)
  2. Utilice el Administrador de procesos para escanear la lista de procesos para los procesos cuyo identificador del paquete que coincida con el que usted está buscando. El identificador del paquete no es inmutable, pero es más difícil de cambiar que el nombre o la dirección.
  3. Si usted está mirando para ver cuando alguien se queda una segunda copia de sí mismo, puede utilizar CFNotificationCenter:

    1. Añadir sí mismo como un observador de “com.yourdomain.yourappname.LaunchResponse”.
    2. Publicar una notificación bajo el nombre de “com.yourdomain.yourappname.LaunchCall”.
    3. Añadir sí mismo como un observador de “com.yourdomain.yourappname.LaunchCall”.

    En su devolución de llamada de observación para la notificación de llamada, enviar la notificación de respuesta.
    En su observación de devolución de llamada para la notificación de respuesta, salida.

    Por lo tanto, cuando el primer proceso empieza, se llamará y no obtiene respuesta; cuando el segundo proceso se inicia, se llamará, obtener una respuesta del primer proceso, y salida en deferencia a la primera.

Esta es una combinación de respuestas romanos y Jeff para Swift 2.0: Si otra instancia de la aplicación con el mismo ID de paquete ya se está ejecutando, muestra una alerta, activar la otra instancia y salir de la instancia duplicada

.
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)
    }

    /* ... */
}

¿Qué hay de IPC ? Se podría abrir un socket y negociar con la otra instancia lanzado. Habría que tener cuidado sin embargo, que funciona si ambas aplicaciones se encienden al mismo tiempo.

No puedo proporcionarle código de ejemplo, ya que no tengo (todavía, pero pronto voy a) usado.

Esta es una versión de SEB de Swift 3.0 . Si otra instancia de la aplicación con el mismo ID de paquete ya se está ejecutando, muestra una alerta, active la otra instancia y salga de la instancia duplicada

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)
         }   
       /* ... */
}

detectar si la aplicación con la misma BundleID se está ejecutando, activarlo y cerrar lo que comienza.

- (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;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top