通常,OS X上的应用程序包只能启动一次,但只需复制捆绑包即可启动两次相同的应用程序。检测和阻止这种可能性的最佳策略是什么?
在Windows上,这种效果可以通过应用程序在启动时创建命名资源,然后在无法创建命名资源时退出,表明正在运行已创建相同资源的另一个进程来实现。当应用程序退出时,这些资源在Windows上以可靠的方式发布。
我在研究这个问题时遇到的问题是OS X上的API会在文件系统中保持状态,从而使得在Windows上使用的策略不可靠,即在不正确的退出后延迟文件可能会错误地指示应用程序已在运行
我可以用来在OS X上实现相同效果的API是:posix,carbon和boost。
想法?
答案 0 :(得分:27)
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/deduplicating-running-instances-or-how-to-detect-if。
答案 1 :(得分:8)
低级解决方案是使用flock()。
每个实例都会尝试在启动时锁定文件,如果锁定失败,则另一个实例已在运行。当您的程序退出时,会自动释放Flocks,因此不必担心过时的锁定。
请注意,无论您选择何种解决方案,都需要有意识地决定拥有“多个实例”的含义。具体来说,如果多个用户同时运行您的应用,那可以吗?
答案 2 :(得分:8)
有一个神秘的Info.plist密钥叫做#34;应用程序禁止多个实例,"但它似乎并不适合我。我正在编写一个CLI应用程序并从一个包中执行它。也许它可以在GUI应用程序中运行,但我还没有尝试过。
答案 3 :(得分:4)
正如已经提到的,Cocoa应用程序通常不允许您一次运行多个实例。
一般来说,一个cocoa方法可以解决这个问题,看看NSWorkspace中的launchApplications。这将返回一个NSArray,其中包含每个已启动应用程序的字典。您可以遍历数组以查看您要查找的应用程序是否已在运行。我建议您使用值为NSApplicationBundleIdentifier的值,该值将具有“com.mycompany.myapp”之类的值,而不是查找名称。如果您需要查找应用程序的软件包标识符,可以查看应用程序包中的info.plist文件。
答案 4 :(得分:3)
首先,它是“Mac OS X”或“OS X”。没有“OS / X”这样的东西。
其次,Mac OS X没有附带Boost;你需要将它与你的应用程序捆绑在一起。
第三,大多数碳不能用于64位。这是一个明确的信号,即某些部分的碳将在某一天消失(当苹果放弃其硬件中的32位时)。迟早,你将不得不用Cocoa重写你的应用程序或放弃Mac。
通常情况下,OS / X上的应用程序包只能启动一次,但只需重命名捆绑包就可以启动两次相同的应用程序。
不,不能。启动重命名或移动的应用程序只会激活(带到前面)已经运行的进程;它不会在第一个过程中开始新的第二个过程。
有几种方法可以判断应用程序是否已在运行。在每种情况下,您都会在启动时执行此操作:
如果您想查看有人自己运行第二份副本,您可以使用CFNotificationCenter:
在您的观察呼叫回叫中,发布响应通知 在您的回应通知的观察回调中,退出。
因此,当第一个进程启动时,它将调用并得不到响应;当第二个进程开始时,它将调用,从第一个进程获得响应,然后以第一个进程的顺序退出。
答案 5 :(得分:1)
IPC怎么样?您可以打开套接字并与其他已启动的实例进行协商。但是,你必须要小心,如果两个应用程序同时启动它会起作用。
我无法为您提供示例代码,因为我还没有(但我会很快)使用它。
答案 6 :(得分:1)
这是Romans和Jeff对Swift 2.0的回答的组合:如果具有相同捆绑ID的另一个应用实例已经在运行,则显示警报,激活另一个实例并退出重复实例。
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)
}
/* ... */
}
答案 7 :(得分:1)
这是 Swift 3.0 的seb版本:如果具有相同软件包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)
}
/* ... */
}
答案 8 :(得分:0)
检测具有相同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;
}