macos - salida - Cómo detectar si una aplicación OS X ya está lanzada
mac no abre aplicaciones (9)
¿Qué pasa con IPC ? Podría abrir un socket y negociar con la otra instancia iniciada. Sin embargo, deberías tener cuidado de que funcione si ambas aplicaciones se inician al mismo tiempo.
No puedo proporcionarle el código de muestra, ya que no lo he utilizado (todavía lo haré pronto).
Normalmente, un paquete de aplicaciones en OS X solo puede iniciarse una vez, sin embargo, simplemente copiando el paquete, la misma aplicación se puede iniciar dos veces. ¿Cuál es la mejor estrategia para detectar y detener esta posibilidad?
En Windows, este efecto puede lograrse simplemente si la aplicación crea un recurso con nombre en el inicio y luego sale si no se puede crear el recurso con nombre, lo que indica que se está ejecutando otro proceso que ya ha creado el mismo recurso. Estos recursos se lanzan de manera confiable en Windows cuando se cierra la aplicación.
El problema que he visto al investigar esto es que las API en OS X se mantienen en el sistema de archivos y, por lo tanto, hacen que la estrategia utilizada en Windows no sea confiable, es decir, los archivos persistentes después de una salida incorrecta pueden indicar falsamente que la aplicación ya se está ejecutando.
Las API que puedo usar para lograr el mismo efecto en OS X son: posix, carbon y boost.
Ideas?
Como ya se ha mencionado, las aplicaciones Cocoa generalmente no le permiten ejecutar más de una instancia a la vez.
En general, una forma de cacao para resolver este aspecto de las aplicaciones iniciadas en NSWorkspace. Esto devuelve una NSArray que contiene un diccionario para cada aplicación iniciada. Puede recorrer la matriz para ver si la aplicación que está buscando ya se está ejecutando. Le aconsejaría que utilice el valor con la clave NSApplicationBundleIdentifier, que tendrá un valor como "com.mycompany.myapp" en lugar de buscar el nombre. Si necesita encontrar el identificador de paquete para una aplicación, puede consultar su archivo info.plist en el paquete de la aplicación.
En primer lugar, es "Mac OS X" o "OS X". No hay tal cosa como "OS / X".
Segundo, Mac OS X no viene con Boost; necesitarías empaquetarlo con tu aplicación.
En tercer lugar, la mayor parte del carbono no está disponible en 64 bits. Esta es una señal clara de que esas partes de Carbon desaparecerán algún día (cuando Apple abandone 32 bits en su hardware). Tarde o temprano, tendrás que volver a escribir tu aplicación con Cocoa o abandonar la Mac.
Normalmente, un paquete de aplicaciones en OS / X solo puede iniciarse una vez, sin embargo, simplemente cambiando el nombre del paquete, la misma aplicación se puede iniciar dos veces.
No, no puede. Lanzar la aplicación renombrada o movida simplemente activará (traerá al frente) el proceso que ya se estaba ejecutando; no iniciará un segundo proceso junto con el primero.
Hay varias formas de saber si una aplicación ya se está ejecutando. En cada caso, haces esto en el lanzamiento:
- Utilice la conexión NS de Cocoa para registrar una conexión con un solo nombre de constante. Esto fallará si el nombre ya está registrado. (Puedes usar Foundation desde una aplicación de Carbon; es el kit de aplicación con el que debes tener cuidado).
- Utilice el Administrador de procesos para escanear la lista de procesos en busca de procesos cuyo identificador de paquete coincida con el que está buscando. El identificador del paquete no es inmutable, pero es más difícil de cambiar que el nombre de archivo o la ubicación.
Si desea ver cuándo alguien ejecuta una segunda copia de sí mismo, puede usar CFNotificationCenter:
- Agregue usted mismo como observador para "com.yourdomain.yourappname.LaunchResponse".
- Publique una notificación con el nombre "com.yourdomain.yourappname.LaunchCall".
- Agregue usted mismo como observador para "com.yourdomain.yourappname.LaunchCall".
En su devolución de llamada de observación para la notificación de llamada, publique la notificación de respuesta.
En su devolución de llamada de observación para la notificación de respuesta, salga.Por lo tanto, cuando se inicia el primer proceso, llamará y no obtendrá ninguna respuesta; cuando comience el segundo proceso, Llamará, obtendrá una Respuesta del primer proceso y saldrá en deferencia al primero.
Esta es una combinación de las respuestas de Romans y Jeff para Swift 2.0: si ya se está ejecutando otra instancia de la aplicación con el mismo ID de paquete, muestre 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 = 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)
}
/* ... */
}
Esta es una versión de seb para Swift 3.0 : si ya se está ejecutando otra instancia de la aplicación con el mismo ID de paquete, muestre 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)
}
/* ... */
}
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];
}
}
Consulte http://blog.jseibert.com/post/1167439217/deduplicating-running-instances-or-how-to-detect-if para obtener más información.
Hay una misteriosa clave Info.plist llamada "La aplicación prohíbe varias instancias", pero no parece funcionar para mí. Estoy escribiendo una aplicación CLI y ejecutándola desde dentro de un paquete. Tal vez funcionaría en una aplicación GUI, pero no lo he intentado.
Una solución de bajo nivel es usar flock ().
Cada instancia intentaría bloquear un archivo en el inicio, y si el bloqueo falla, entonces ya se está ejecutando otra instancia. Las bandadas se liberan automáticamente cuando el programa sale, por lo que no se preocupa por los bloqueos antiguos.
Tenga en cuenta que cualquiera sea la solución que elija, debe tomar una decisión consciente sobre lo que significa tener "múltiples instancias". Específicamente, si varios usuarios ejecutan su aplicación al mismo tiempo, ¿está bien?
detecta si la aplicación con el mismo bundleID se está ejecutando, actívala y cierra 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;
}