Pergunta

Normalmente, um pacote de aplicativos no Mac OS X só pode ser iniciado uma vez, no entanto, simplesmente copiando o pacote a mesma aplicação pode ser lançado duas vezes. Qual é a melhor estratégia para detectar e parar essa possibilidade?

No Windows este efeito pode simplesmente ser alcançado pela aplicação criando um recurso nomeado no lançamento e, em seguida, sair se o recurso nomeado não pode ser criado, indicando que outro processo está em execução que já criou o mesmo recurso. Estes recursos são liberados em uma maneira confiável no Windows quando o aplicativo é encerrado.

O problema que tenho visto quando pesquisando esse é que as APIs no Mac OS X manter o estado no sistema de arquivos e, portanto, faz com que a estratégia utilizada em janelas não confiável, ou seja remanescentes arquivos após uma saída imprópria falsamente pode indicar que o aplicativo já está em execução .

As APIs que posso usar para conseguir o mesmo efeito no OS X são: posix, carbono e impulso

.

Idéias?

Foi útil?

Solução

Uma solução de baixo nível é a utilização de rebanho ().

Cada instância iria tentar bloquear um arquivo na inicialização, e se o bloqueio falhar, em seguida, outra instância já está em execução. Rebanhos são automagicamente liberada quando da saída do programa, de modo que não se preocupa com travas perdidas.

Note que qualquer que seja a solução escolhida, você precisa tomar uma decisão consciente sobre o que significa ter "várias instâncias". Especificamente, se vários usuários estão executando o aplicativo ao mesmo tempo, é que ok?

Outras dicas

Este é extremamente fácil no 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: // blogue .jseibert.com / post / 1167439217 / desduplicação-executando-instances-ou-how-to-detectar-se para obter mais informações.

Há uma chave Info.plist misteriosa chamada "Aplicação proíbe várias instâncias", mas não parece trabalhar para mim. Estou escrevendo um aplicativo CLI e executá-lo de dentro de um pacote. Talvez ele iria trabalhar em uma aplicação GUI, mas eu não tentei.

Como já foi mencionado aplicações Cocoa geralmente não permitem que você execute mais de uma instância de cada vez.

Em geral, uma maneira de cacau para resolver este olhada launchedApplications em NSWorkspace. Isto devolve um NSArray contendo um dicionário para cada aplicação lançado. Você pode percorrer a matriz para ver se o aplicativo que você está procurando já está em execução. Aconselho que você use o valor com a tecla NSApplicationBundleIdentifier que terá um valor como "com.mycompany.myapp" em vez de olhar para o nome. Se você precisa encontrar o identificador de pacote para um aplicativo que você pode olhar para seu arquivo info.plist no pacote de aplicativo.

Primeiro, é de “Mac OS X” ou “OS X”. Não existe tal coisa como “OS / X”.

Em segundo lugar, não Mac OS X não vem com impulso; você precisa empacotá-lo com o seu aplicativo.

Em terceiro lugar, a maior parte do carbono não está disponível em 64-bit. Este é um sinal claro de que aquelas porções de carbono vai embora um dia (quando a Apple abandona de 32 bits na sua hardware). Mais cedo ou mais tarde, você terá que quer reescrever seu aplicativo com cacau ou abandonar o Mac.

Normalmente, um pacote de aplicativos no Mac OS / X só pode ser iniciado uma vez, no entanto, simplesmente mudar o nome do pacote a mesma aplicação pode ser lançado duas vezes.

Não, não pode. Iniciando o aplicativo renomeados ou movidos simplesmente ativar (trazer para a frente) o processo que já estava correndo; ele não irá iniciar um novo segundo processo, ao lado do primeiro.


Existem várias maneiras de dizer se um aplicativo já está em execução. Em cada caso, você faz isso no lançamento:

  1. NSConnection de Uso Cacau para registrar uma conexão com um único nome constante. Isto irá falhar se o nome já está registrado. (Você pode usar Foundation a partir de um aplicativo de carbono;. É o Kit de aplicação você tem que ter cuidado com)
  2. Use o Process Manager para examinar a lista de processo para processos cujo identificador de pacote coincidir com o que você está procurando. O identificador de pacote não é imutável, mas é mais difícil de mudar do que o nome do arquivo ou localização.
  3. Se você estiver olhando para ver quando alguém executa uma segunda cópia de si mesmo, você pode usar CFNotificationCenter:

    1. Adicione-se como um observador para “com.yourdomain.yourappname.LaunchResponse”.
    2. Depois de uma notificação com o nome “com.yourdomain.yourappname.LaunchCall”.
    3. Adicione-se como um observador para “com.yourdomain.yourappname.LaunchCall”.

    Em seu retorno observação para a notificação de chamadas, postar a notificação Response.
    Em seu retorno de observação para a notificação de resposta, saída.

    Assim, quando o primeiro processo é iniciado, ele vai ligar e obter nenhuma resposta; quando o segundo processo é iniciado, ele irá chamar, obter uma resposta do primeiro processo, e sair em deferência ao primeiro.

Esta é uma combinação de romanos e respostas de Jeff para Swift 2.0:. Se outra instância do aplicativo com o mesmo ID pacote já está em execução, mostrar um alerta, ativa a outra instância e sair da instância 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)
    }

    /* ... */
}

E sobre IPC ? Você poderia abrir um socket e negociar com a outra instância lançado. Você teria que ser cuidado, porém, que ele funciona se ambos os aplicativos começam ao mesmo tempo.

Eu não posso fornecer-lhe o código de exemplo, como eu não tem (ainda, mas em breve vou) usou-o.

Esta é uma versão do seb de para Swift 3.0 :. Se outra instância do aplicativo com o mesmo ID pacote já está em execução, mostrar um alerta, ativa a outra instância e sair da instância 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 se a aplicação com o mesmo BundleID está em execução, ativá-lo e fechar o que começa.

- (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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top