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

Macos 如何检测OS X应用程序是否已启动,macos,boost,resources,macos-carbon,launch,Macos,Boost,Resources,Macos Carbon,Launch,通常,OS X上的应用程序捆绑包只能启动一次,但是,通过简单地复制捆绑包,同一应用程序可以启动两次。检测和阻止这种可能性的最佳策略是什么 在Windows上,此效果可以通过应用程序在启动时创建命名资源来实现,如果无法创建命名资源,则退出,这表示正在运行另一个已创建相同资源的进程。当应用程序退出时,这些资源在Windows上以可靠的方式释放 我在研究这一点时看到的问题是,OS X上的API在文件系统中保持状态,从而使windows上使用的策略不可靠,即在不正确的退出后,文件的延迟可能会错误地表明应

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

在Windows上,此效果可以通过应用程序在启动时创建命名资源来实现,如果无法创建命名资源,则退出,这表示正在运行另一个已创建相同资源的进程。当应用程序退出时,这些资源在Windows上以可靠的方式释放

我在研究这一点时看到的问题是,OS X上的API在文件系统中保持状态,从而使windows上使用的策略不可靠,即在不正确的退出后,文件的延迟可能会错误地表明应用程序已经在运行

我可以在OSX上使用的API有:posix、carbon和boost

想法?

怎么样?您可以打开一个套接字并与另一个启动的实例协商。不过,你必须小心,如果两个应用程序同时启动,它就会起作用


我无法提供示例代码,因为我还没有(但我很快就会)使用它。

首先,它是“MacOSX”或“OSX”。没有“OS/X”这样的东西

第二,MacOSX没有附带Boost;您需要将其与应用程序捆绑在一起

第三,大部分碳元素在64位中不可用。这是一个明确的信号,表明这些碳元素有一天会消失(当苹果放弃其32位硬件时)。迟早,你要么用Cocoa重写你的应用程序,要么放弃Mac

通常,OS/X上的应用程序捆绑包只能启动一次,但是,通过简单地重命名捆绑包,相同的应用程序可以启动两次

不,不能。启动重命名或移动的应用程序只需激活(放在前面)已经运行的进程;它不会在第一个流程的基础上启动新的第二个流程


有几种方法可以判断应用程序是否已经在运行。在每种情况下,您都可以在启动时执行以下操作:

  • 使用Cocoa的NSConnection以单个常量名称注册连接。如果名称已注册,则此操作将失败。(你可以从碳应用程序中使用基础,这是你必须小心的应用程序包)
  • 使用Process Manager扫描流程列表,查找其捆绑包标识符与您要查找的捆绑包标识符匹配的流程。捆绑包标识符不是不可更改的,但它比文件名或位置更难更改
  • 如果您希望看到有人何时运行您自己的第二个副本,您可以使用CFNotificationCenter:

  • 将自己添加为“com.yourdomain.yourappname.LaunchResponse”的观察者
  • 以“com.yourdomain.yourappname.LaunchCall”的名义发布通知
  • 将自己添加为“com.yourdomain.yourappname.LaunchCall”的观察者
  • 在呼叫通知的观察回调中,发布响应通知。
    在响应通知的观察回调中,退出

    因此,当第一个进程启动时,它将调用并没有得到响应;当第二个进程启动时,它将调用,从第一个进程获得响应,并根据第一个进程退出


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

    每个实例将尝试在启动时锁定一个文件,如果锁定失败,则另一个实例已在运行。程序退出时,羊群会自动释放,所以不用担心过时的锁


    请注意,无论您选择什么解决方案,您都需要有意识地决定“多个实例”的含义。具体来说,如果多个用户同时运行您的应用程序,可以吗?

    正如前面提到的,Cocoa应用程序通常不允许您一次运行多个实例


    通常,解决此问题的一种方法是在NSWorkspace中启动应用程序。这将返回一个NSArray,其中包含每个已启动应用程序的字典。您可以在阵列中循环,查看您正在查找的应用程序是否已在运行。我建议您将该值与键NSApplicationBundleIdentifier一起使用,该键的值类似于“com.mycompany.myapp”,而不是查找名称。如果您需要查找应用程序的捆绑标识,可以查看应用程序包中的info.plist文件。

    这在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];
        }
    }
    

    有关更多信息,请参阅。

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

    检测具有相同bundleID的应用程序是否正在运行,激活它并关闭启动的程序

    - (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;
        }
    
    -(id)的init方法
    NSArray*应用程序=[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle]bundleIdentifier]];
    如果([应用计数]>1)
    {
    NSRunningApplication*curApp=[NSRunningApplication currentApplication];
    用于(NSRunningApplication*应用程序中的应用程序)
    {
    如果(app!=curApp)
    {
    [应用程序激活选项:非应用程序激活所有窗口|非应用程序激活信号或其他应用程序];
    打破
    }
    }
    [NSApp终止:无];
    返回零;
    }
    
    这是Romans和Jeff对Swift 2.0的回答的组合:如果具有相同捆绑ID的应用程序的另一个实例已在运行,则显示警报,激活另一个实例并退出重复实例

    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)
             }   
           /* ... */
    }
    
    func applicationdFinishLaunching(通知:NSNotification){
    /*检查此应用程序的另一个实例是否正在运行*/
    让bundleID=NSBundle.mainBundle(