如何检测OS X应用程序是否已启动

Per*_*son 20 macos resources boost macos-carbon launch

通常,OS X上的应用程序包只能启动一次,但是通过简单地复制捆绑包,可以启动两次相同的应用程序.检测和阻止这种可能性的最佳策略是什么?

在Windows上,这种效果可以通过应用程序在启动时创建命名资源,然后在无法创建命名资源时退出,表明正在运行已创建相同资源的另一个进程来实现.当应用程序退出时,这些资源在Windows上以可靠的方式发布.

我在研究这个问题时遇到的问题是OS X上的API会在文件系统中保持状态,从而使得在Windows上使用的策略不可靠,即在不正确的退出后延迟文件可能会错误地指示应用程序已在运行.

我可以用来在OS X上实现相同效果的API是:posix,carbon和boost.

想法?

小智 28

这在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];
    }
}
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅http://blog.jseibert.com/post/1167439217/deduplicating-running-instances-or-how-to-detect-if.

  • 在OS 10.8中,在main.m中启动时执行此检查不起作用,因为在该阶段正在运行的应用程序本身不在数组中(可能只在下一个runloop中注册),所以而不是"> 1"你必须检查"> 0".要在未来版本中使用此安全版本,最好显式检查当前应用程序的数组:`for(运行应用程序中的NSRunningApplication*runningApp){if(![runningApp isEqual:[NSRunningApplication currentApplication]]){// Alert and exit} }` (2认同)
  • 一个更重要的问题:runningApplicationsWithBundleIdentifier返回与bundleID匹配的正在运行的应用程序,但最重要的是仅返回当前用户拥有的应用程序(因此这些解决方案不会阻止此计算机上的不同用户同时运行您的应用程序. (2认同)

小智 9

一个低级解决方案是使用flock().

每个实例都会尝试在启动时锁定文件,如果锁定失败,则另一个实例已在运行.当您的程序退出时,会自动释放Flocks,因此不必担心过时的锁定.

请注意,无论您选择何种解决方案,您都需要有意识地决定拥有"多个实例"的含义.具体来说,如果多个用户同时运行您的应用,那可以吗?


Raf*_*ian 8

有一个神秘的Info.plist键称为"应用程序禁止多个实例",但它似乎对我不起作用.我正在编写一个CLI应用程序并从一个包中执行它.也许它可以在GUI应用程序中工作,但我还没有尝试过.

  • 从Launchpad或Finder启动应用程序时,此键(LSMultipleInstancesProhibited)运行良好.作为奖励,已经运行的应用程序被带到了前面.对我来说,这比显示错误对话框更好.从命令行启动应用程序时,该键不起作用. (4认同)