8

On Windows, it's common practice to create a named mutex and use the presence of that to determine that an instance of a given app is already running. This has its drawbacks, but mostly works.

I can think of a ways to do this on the Mac:

  1. named pthread mutexes
  2. enumerate running processes and look for one that matches
  3. create and lock a file

Is there something built into Cocoa/Carbon that's easier than the options above? If not, which of the three are most used on the mac? I would assume 2 or 3...

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
psychotik
  • 38,153
  • 34
  • 100
  • 135
  • just looking at how apps are launched and interacted with I didn't think it was possible to have multiple instances... At least not with apps bundled up into a nice .app – Cogsy Jul 04 '09 at 00:48
  • Ok I cheated a little in my question... I really wanted to run a test app that did a bunch of validation but launching it while the application is running caused false positives. In order to not waste time chasing down useless issues, I wanted the test app to bail with an error if the app is running. On windows, I solved it using the single-instancing approach and assumed I want to do the same for the mac. – psychotik Jul 06 '09 at 02:36
  • [used NSRunningApplication detect if application with same bundleID is running, activate it and close what starts.][1] [1]: http://stackoverflow.com/questions/684911/how-to-detect-whether-an-os-x-application-is-already-launched/23775478#23775478 – Roman Solodyashkin May 23 '14 at 07:36

7 Answers7

9

To elaborate further on using NSWorkspace. Try using launchedApplications in NSWorkspace. This returns an NSArray containing a dictionary for each launched application. You can loop through the array to see if the app you are looking for is already running. I would advise that you use the value with the key NSApplicationBundleIdentifier which will have a value like "com.mycompany.myapp" rather than looking for the name. If you need to find the bundle identifier for an app you can look at its info.plist file in the app package.

Jon Steinmetz
  • 4,104
  • 1
  • 23
  • 21
  • This is kinda what I want to do, but my test app isn't a Cocoa app (see updated comment in my Q above). I found GetNextProcess() in Carbon that might help.. any other suggestions? – psychotik Jul 06 '09 at 02:38
  • That would be the way to do it if you are a Carbon app. Are you already linked against the Carbon library? However, the same single instance feature applies to Carbon apps as well. It sounds like you might be building a unix command line program, is that correct? It might be helpful to describe what you are building in more detail. – Jon Steinmetz Jul 07 '09 at 01:54
  • Note that `NSWorkspace -launchedApplications` was depreciated in Mac OS X 10.7. Apple recommends that you now use `NSWorkspace -runningApplications` instead (10.6+): http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html#//apple_ref/occ/instm/NSWorkspace/runningApplications – Dave Nov 09 '11 at 17:23
6

Macs do not have instances in the same way as Windows does. Generally speaking you want the application running twice you physically need to copy the binary and then double click on the copied version as well.

If you need two instances of an application running then you are not thinking like a Mac user :).

Edit: This is technically not true. Check the comments.

Nikolay Tsenkov
  • 1,128
  • 10
  • 26
Cromulent
  • 3,788
  • 4
  • 31
  • 41
  • 5
    In other words, there's no need if we're talking about GUI apps. It already works that way. – Chuck Jul 04 '09 at 16:33
  • 19
    That's not true. If you start the application from the terminal, for example, you can launch _N_ instances simultaneously. The fact that it is _uncommon_ do not mean that the case should not be taken into account. – Dev Oct 08 '12 at 06:58
  • Right. I even will give you a simple example, which I believe is a pretty common one: starting an app from postinstall of an installer. If you search the app and launch from Spotlight, second instance lights up. – Nikolay Tsenkov Jul 10 '14 at 11:52
1

Mapping process management among disparate operating systems doesn't work. Or doesn't work well. By default and without particular effort, you get one copy and only one copy of the application.

Here's a previous similar question that goes a step further than this current question, and with some replies that discuss interlocking when there are multiple copies of an image, or multiple applications that need coordination.

How to detect whether an OS X application is already launched

For an introduction to the run-time context and particularly around Mac OS X daemons and agents (and for those cases when you do need to have multiple copies of an executable running, as a pool or such and akin to Apache), see:

http://developer.apple.com/technotes/tn2005/tn2083.html

Community
  • 1
  • 1
Stephen Hoffman
  • 312
  • 2
  • 4
1

If you're writing a Cocoa application, you can use NSWorkspace to see if another process is running with your bundle identifier. I've seen a few apps that present a dialog and say: "An instance of this app is already running" - I think Firefox does it, actually.

It's not a very "mac-ish" approach, but it will get the job done.

Ben Gotow
  • 14,805
  • 3
  • 42
  • 47
  • This is kinda what I want to do, but my test app isn't a Cocoa app (see updated comment in my Q above). I found GetNextProcess() in Carbon that might help.. any other suggestions? – psychotik Jul 06 '09 at 02:37
0

If you were to deploy your application with Java Web Start (JWS), you could use javax.jnlp.SingleInstanceService. JWS provisioning would also provide automatic program updates.

David J. Liszewski
  • 10,959
  • 6
  • 44
  • 57
0

The following code can be used the quit applications with the same bundle identifier that are already running.

It also displays an alert after doing so.

AppDelegate.applicationDidFinishLaunching

let runningApp =
    NSWorkspace.shared.runningApplications
        .filter { item in item.bundleIdentifier == Bundle.main.bundleIdentifier }
        .first { item in item.processIdentifier != getpid() }

if let running = runningApp {
    running.forceTerminate()
    
    let alert = NSAlert()
    alert.messageText = "App was alreday running"
    alert.informativeText = "App was terminated."
    alert.alertStyle = NSAlert.Style.informational
    alert.addButton(withTitle: "OK")
    alert.runModal()
}

note: This assumes that there only can be one already running app. Should be trivial to adapt for other scenarios.

JaggerJo
  • 734
  • 4
  • 15
0

Here's the Swift 5 code for quitting current application if one is already running. Just put it in applicationDidFinishLaunching

let bundleIdentifier = Bundle.main.bundleIdentifier

if NSWorkspace.shared.runningApplications.filter { $0.bundleIdentifier == bundleIdentifier }.count > 1 {
    print("App already running.")
    exit(0)
}

Raimundas Sakalauskas
  • 2,016
  • 21
  • 24