Making an Application a Singleton

To make sure there is only one instance of your application running, you can use the function WeAreAlreadyRunning() from SingletonApplication.zip
WeAreAlreadyRunning() walks the list of running processes, using the Process Manager. Each process’s bundle identifier (if it exists) is compared to the bundle identifier of the calling process’s main bundle.

If you are writing a tool that does not have a bundle identifier, then you can change the function to compare the name of the process. (CopyProcessName gets a process name.) However, unlike with bundle identifiers, there is no guarantee that process names will be unique. Proceed with caution. Other possible identifiers, such as version number, and path are listed in the Process Manager documentation..

The function:
/* requirements: Carbon.framework, IOKit.framework, and the calling process must have a main bundle. */
int WeAreAlreadyRunning(void)
{
int foundDuplicateProcess = 0;
ProcessSerialNumber PSN = {kNoProcess};
ProcessSerialNumber ourPSN = {kNoProcess};
CFStringRef ourBundleID = CFBundleGetIdentifier(CFBundleGetMainBundle());
assert(ourBundleID);//Prehaps you are using this function in a command-line tool that does not have a bundle?
CFRetain(ourBundleID); //just in case.
GetCurrentProcess(&ourPSN);
while(!foundDuplicateProcess && GetNextProcess(&PSN) == noErr)
{
Boolean sameProcess;
OSStatus err = SameProcess(&PSN, &ourPSN, &sameProcess);
if( !err && !sameProcess )
{
CFDictionaryRef info = ProcessInformationCopyDictionary(&PSN,kProcessDictionaryIncludeAllInformationMask);
if(info)
{
CFStringRef bundleID = CFDictionaryGetValue(info, kIOBundleIdentifierKey);
foundDuplicateProcess = (bundleID && CFStringCompare(ourBundleID, bundleID, 0) == kCFCompareEqualTo);
CFRelease(info);
}
}
}
CFRelease(ourBundleID);
return foundDuplicateProcess;
}

The Xcode project: SingletonApplication.zip

EDITED TO ADD (10/4/2007): bug found: if another instance of the process was running, but has been killed and is now a zombie, then WeAreAlreadyRunning() will report YES, even though there is no other active instance.

EDITED TO ADD: sometimes you can do this using only Cocoa, but your program can’t be a background process, otherwise launchedApplications won’t find it, since it returns a list of programs that show up in the Dock, not a full list of running processes.

NSPredicate *hasOurBundleID = [NSPredicate predicateWithFormat:@"%K == %@", @"NSApplicationBundleIdentifier", [[NSBundle mainBundle] bundleIdentifier]];
BOOL duplicateProcess = 1 < [[[[NSWorkspace sharedWorkspace] launchedApplications] filteredArrayUsingPredicate:hasOurBundleID] count];

Advertisements
Explore posts in the same categories: Cocoa, MacOSX, Programming, Sample Code

2 Comments on “Making an Application a Singleton”

  1. bob Says:

    CFStringCompare(ourBundleID, bundleID, 0) == kCFCompareEqualTo

    should be written as

    CFEqual(ourBundleID, bundleID)

  2. Florian Says:

    I blog often and I seriously thank you for your content.
    This article has truly peaked my interest. I will book mark your blog and keep checking for new
    details about once a week. I subscribed to your Feed as well.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: