Relaunching an application

  • Hi everyone,

    I have an application that I need to restart. An easy way to do this
    could be:

    [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    [NSApp terminate:self];

    The problem is that the above solution results in confusion in the
    system dock; sometimes the original application does manage to quit
    before the new instance, other times not. If it does NOT manage to
    quit before the new one is launched, the dock ends up with two icons
    for my application; one that indicates that the application is
    launched, another one that's indicates that it's not launched. This is
    NOT good.

    I've seen other programs in need of a restart (e.g. the Sparkle
    framework) using a separate helper application that simply checks
    whether the original process has quit properly before re-launching it.
    This sure does work, but it isn't a pretty solution.

    Another solution could be something like:

    system("nohup sh -c 'sleep 1; open -b com.company.identifier'");
    exit(0);

    This is (in my humble opinion) better, but still, not very elegant.

    Wouldn't it be nice if we could have something like [NSApp relaunch],
    [NSApp relaunchWithArguments:argArray] etc. to deal with stuff like
    this? Or does such calls imply things that I'm overseeing right now?
    Maybe there are similar methods already available in the system, that
    accomplishes stuff like this?

    Any ideas/suggestions on the above are more than welcome.

    Thanks in advance.
    Mattias
  • On Mar 3, 2008, at 18:24, Mattias Arrelid wrote:

    > I have an application that I need to restart.

    Why do you want to do that?

    > An easy way to do this could be:
    >
    > [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    > [NSApp terminate:self];
    >
    > The problem is that the above solution results in confusion in the
    > system dock; sometimes the original application does manage to quit
    > before the new instance, other times not. If it does NOT manage to
    > quit before the new one is launched, the dock ends up with two
    > icons for my application; one that indicates that the application
    > is launched, another one that's indicates that it's not launched.
    > This is NOT good.
    >
    > I've seen other programs in need of a restart (e.g. the Sparkle
    > framework) using a separate helper application that simply checks
    > whether the original process has quit properly before re-launching
    > it. This sure does work, but it isn't a pretty solution.

    Seems pretty to me. You want use a separate process to make this work
    reliably.

    Best Regards,

    Nir Soffer
  • On 3 mar 2008, at 18.14, Nir Soffer wrote:

    > On Mar 3, 2008, at 18:24, Mattias Arrelid wrote:
    >
    >> I have an application that I need to restart.
    >
    > Why do you want to do that?

    A scenario could be that the entire contents of the .app bundle has
    been replaced with new stuff (e.g. the case with Sparkle).

    >> An easy way to do this could be:
    >>
    >> [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    >> [NSApp terminate:self];
    >>
    >> The problem is that the above solution results in confusion in the
    >> system dock; sometimes the original application does manage to quit
    >> before the new instance, other times not. If it does NOT manage to
    >> quit before the new one is launched, the dock ends up with two
    >> icons for my application; one that indicates that the application
    >> is launched, another one that's indicates that it's not launched.
    >> This is NOT good.
    >>
    >> I've seen other programs in need of a restart (e.g. the Sparkle
    >> framework) using a separate helper application that simply checks
    >> whether the original process has quit properly before re-launching
    >> it. This sure does work, but it isn't a pretty solution.
    >
    > Seems pretty to me. You want use a separate process to make this
    > work reliably.

    It works, but I'd say it was prettier if there was a framework call to
    accomplish this.

    Regards
    Mattias
  • Le 3 mars 08 à 18:18, Mattias Arrelid a écrit :

    > On 3 mar 2008, at 18.14, Nir Soffer wrote:
    >
    >> On Mar 3, 2008, at 18:24, Mattias Arrelid wrote:
    >>
    >>> I have an application that I need to restart.
    >>
    >> Why do you want to do that?
    >
    > A scenario could be that the entire contents of the .app bundle has
    > been replaced with new stuff (e.g. the case with Sparkle).
    >
    >>> An easy way to do this could be:
    >>>
    >>> [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    >>> [NSApp terminate:self];
    >>>
    >>> The problem is that the above solution results in confusion in the
    >>> system dock; sometimes the original application does manage to
    >>> quit before the new instance, other times not. If it does NOT
    >>> manage to quit before the new one is launched, the dock ends up
    >>> with two icons for my application; one that indicates that the
    >>> application is launched, another one that's indicates that it's
    >>> not launched. This is NOT good.
    >>>
    >>> I've seen other programs in need of a restart (e.g. the Sparkle
    >>> framework) using a separate helper application that simply checks
    >>> whether the original process has quit properly before re-launching
    >>> it. This sure does work, but it isn't a pretty solution.
    >>
    >> Seems pretty to me. You want use a separate process to make this
    >> work reliably.
    >
    > It works, but I'd say it was prettier if there was a framework call
    > to accomplish this.

    Fill a feature request ;-)
  • Mattias Arrelid wrote:
    > On 3 mar 2008, at 18.14, Nir Soffer wrote:
    >
    >> On Mar 3, 2008, at 18:24, Mattias Arrelid wrote:
    >>
    >>> I have an application that I need to restart.
    >>
    >> Why do you want to do that?
    >
    > A scenario could be that the entire contents of the .app bundle has
    > been replaced with new stuff (e.g. the case with Sparkle).
    >
    >>> An easy way to do this could be:
    >>>
    >>> [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    >>> [NSApp terminate:self];
    >>>
    >>> The problem is that the above solution results in confusion in the
    >>> system dock; sometimes the original application does manage to quit
    >>> before the new instance, other times not. If it does NOT manage to
    >>> quit before the new one is launched, the dock ends up with two icons
    >>> for my application; one that indicates that the application is
    >>> launched, another one that's indicates that it's not launched. This
    >>> is NOT good.
    >>>
    >>> I've seen other programs in need of a restart (e.g. the Sparkle
    >>> framework) using a separate helper application that simply checks
    >>> whether the original process has quit properly before re-launching
    >>> it. This sure does work, but it isn't a pretty solution.
    >>
    >> Seems pretty to me. You want use a separate process to make this work
    >> reliably.
    >
    > It works, but I'd say it was prettier if there was a framework call to
    > accomplish this.

    I bet that if you took some time, you could find a way to do this via
    forking and using low-level BSD calls (non-Window Server) instead of
    launching a full-fledged separate app.

    OTOH, it would probably be easiest to just use the helper app :) Keep in
    mind that this "app" could probably be as simple as a few lines of
    AppleScript.
  • Perhaps a small application like relaunch.app could be imbedded into
    your relaunchable application, where all it pretty much does is serve
    as a buffer by relaunching your application reliably (through Cocoa
    methods) after your app calls it the same way you mentioned above.

    [[NSWorkspace sharedWorkspace] launchApplication:relauncherExecutablePath];
    [NSApp terminate:self];

    Then the relaunch.app program uses a method which calls itself
    continuously (like a loop that wont make your app unresponsive), which
    does a check to see if the original app is still running using
    NSWorkspace's -launchedApplications, and only after it sees your app
    end, then it will run the app again and terminate itself. This ensures
    that your app opens only after it ends first. And doing this would be
    quite easy, using two methods, -awakeFromNib and your custom method
    -hasAppEnded. The former would launch the second one immediately, and
    do nothing else, and the second one would do the check, and at the
    end, if it is still running (hasnt quit and run the new app) it would
    launch itself with [self performSelector:@selector(hasAppEnded)
    withObject:nil afterDelay:1.0]; or something. This is what I would do
    anyway. 1.0 might be too long or too short for a real application,
    though.

    On Mon, Mar 3, 2008 at 10:24 AM, Mattias Arrelid <mattias...> wrote:
    > Hi everyone,
    >
    > I have an application that I need to restart. An easy way to do this
    > could be:
    >
    > [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    > [NSApp terminate:self];
    >
    > The problem is that the above solution results in confusion in the
    > system dock; sometimes the original application does manage to quit
    > before the new instance, other times not. If it does NOT manage to
    > quit before the new one is launched, the dock ends up with two icons
    > for my application; one that indicates that the application is
    > launched, another one that's indicates that it's not launched. This is
    > NOT good.
    >
    > I've seen other programs in need of a restart (e.g. the Sparkle
    > framework) using a separate helper application that simply checks
    > whether the original process has quit properly before re-launching it.
    > This sure does work, but it isn't a pretty solution.
    >
    > Another solution could be something like:
    >
    > system("nohup sh -c 'sleep 1; open -b com.company.identifier'");
    > exit(0);
    >
    > This is (in my humble opinion) better, but still, not very elegant.
    >
    > Wouldn't it be nice if we could have something like [NSApp relaunch],
    > [NSApp relaunchWithArguments:argArray] etc. to deal with stuff like
    > this? Or does such calls imply things that I'm overseeing right now?
    > Maybe there are similar methods already available in the system, that
    > accomplishes stuff like this?
    >
    > Any ideas/suggestions on the above are more than welcome.
    >
    > Thanks in advance.
    > Mattias
    >
  • On Mon, Mar 3, 2008 at 2:43 PM, Steven Degutis <sephknows...> wrote:
    > Perhaps a small application like relaunch.app could be imbedded into
    > your relaunchable application, where all it pretty much does is serve
    > as a buffer by relaunching your application reliably (through Cocoa
    > methods) after your app calls it the same way you mentioned above.
    >
    > [[NSWorkspace sharedWorkspace] launchApplication:relauncherExecutablePath];
    > [NSApp terminate:self];
    >
    > Then the relaunch.app program uses a method which calls itself
    > continuously (like a loop that wont make your app unresponsive), which
    > does a check to see if the original app is still running using
    > NSWorkspace's -launchedApplications, and only after it sees your app
    > end, then it will run the app again and terminate itself. This ensures
    > that your app opens only after it ends first. And doing this would be
    > quite easy, using two methods, -awakeFromNib and your custom method
    > -hasAppEnded. The former would launch the second one immediately, and
    > do nothing else, and the second one would do the check, and at the
    > end, if it is still running (hasnt quit and run the new app) it would
    > launch itself with [self performSelector:@selector(hasAppEnded)
    > withObject:nil afterDelay:1.0]; or something. This is what I would do
    > anyway. 1.0 might be too long or too short for a real application,
    > though.

    The best way is to write a little program like this (error checking
    removed for brevity, typed in mail client, caveat emptor):

    int main(int argc, char **argv)
    {
        char dummy;
        read(STDIN_FILENO, &dummy, 1);

        [NSAutoreleasePool new];
        NSURL *url = [NSURL fileURLWithPath:[NSString
    stringWithUTF8String:argv[1]]];
        LSOpenCFURLRef((CFURLRef)url, NULL);

        return 0;
    }

    Then invoke it using an NSTask in the main app:

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:pathToHelper];
    [task setArguments:[NSArray arrayWithObject:[[NSBundle mainBundle]
    bundlePath]]];
    [task setStandardInput:[NSPipe pipe]];
    [task launch];

    And then quit the app. The subtask will block in the read waiting for
    data on the pipe which will never come. When your app terminates, the
    write end of the pipe closes, unblocking the read in the subtask. The
    subtask then relaunches your app using the URL you gave it as a
    parameter. It's fast, easy to use, and requires no polling or messy
    shell scripts.

    Mike
  • I've come up with an applucation design that works better than my
    previous suggestion of psuedo-polling launchedApplications. Rather,
    the relaunch application subscribes to NSWorkspace's notification
    center, and waits until it gaurantees that its owner-Application has
    terminated. If you want the entire program plus a demo program which
    uses this, go to:

    http://they.dontexist.org/relaunch.zip (source is included, plus a
    universal binary of the demo in action, feel free to use it all you
    want)

    Here is the entire code for relaunch.app:

    #import <Cocoa/Cocoa.h>

    @interface tizzy : NSObject {
    NSString *appPath;
    }

    - (void) gotQuitMessage:(NSNotification*)theNotification;

    @end

    #import "tizzy.h"

    @implementation tizzy

    // in the .nib this object must be included (though feel free to
    delete the NSWindow in there)
    // though im sure theres non-GUI workarounds, too

    - (id) init { // create the appPath which we are waiting for to quit
    if (self = [super init]) {
      [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
    selector:@selector(gotQuitMessage:)
    name:NSWorkspaceDidTerminateApplicationNotification object:nil];
      appPath = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0];
      // we are relaunching whatever application this one lies inside of
      // so we use a loop to get rid of everything except BigApp.app in this:
      // BigApp.app/Contents/Resources/relauncher.app/Contents/MacOS/relauncher
    at least)
      int i; for (i = 0; i < 6; i++) appPath = [appPath
    stringByDeletingLastPathComponent]; (
      [appPath retain];
    }
    return self;
    }

    // when we get the notification, relaunch the app at appPath and quit this one!
    // unfortunately, i still cant figure out how to give focus to that
    "parent" application.
    - (void) gotQuitMessage:(NSNotification*)theNotification {
    NSString *theQuittingApp = [[theNotification userInfo]
    valueForKey:@"NSApplicationPath"];
    if ([appPath isEqualToString:theQuittingApp]) {
      [[NSWorkspace sharedWorkspace] launchApplication:appPath];
      [NSApp terminate:self];
    }
    }

    @end

    On Mon, Mar 3, 2008 at 4:22 PM, Michael Ash <michael.ash...> wrote:
    > On Mon, Mar 3, 2008 at 2:43 PM, Steven Degutis <sephknows...> wrote:
    >> Perhaps a small application like relaunch.app could be imbedded into
    >> your relaunchable application, where all it pretty much does is serve
    >> as a buffer by relaunching your application reliably (through Cocoa
    >> methods) after your app calls it the same way you mentioned above.
    >>
    >> [[NSWorkspace sharedWorkspace] launchApplication:relauncherExecutablePath];
    >> [NSApp terminate:self];
    >>
    >> Then the relaunch.app program uses a method which calls itself
    >> continuously (like a loop that wont make your app unresponsive), which
    >> does a check to see if the original app is still running using
    >> NSWorkspace's -launchedApplications, and only after it sees your app
    >> end, then it will run the app again and terminate itself. This ensures
    >> that your app opens only after it ends first. And doing this would be
    >> quite easy, using two methods, -awakeFromNib and your custom method
    >> -hasAppEnded. The former would launch the second one immediately, and
    >> do nothing else, and the second one would do the check, and at the
    >> end, if it is still running (hasnt quit and run the new app) it would
    >> launch itself with [self performSelector:@selector(hasAppEnded)
    >> withObject:nil afterDelay:1.0]; or something. This is what I would do
    >> anyway. 1.0 might be too long or too short for a real application,
    >> though.
    >
    > The best way is to write a little program like this (error checking
    > removed for brevity, typed in mail client, caveat emptor):
    >
    > int main(int argc, char **argv)
    > {
    > char dummy;
    > read(STDIN_FILENO, &dummy, 1);
    >
    > [NSAutoreleasePool new];
    > NSURL *url = [NSURL fileURLWithPath:[NSString
    > stringWithUTF8String:argv[1]]];
    > LSOpenCFURLRef((CFURLRef)url, NULL);
    >
    > return 0;
    > }
    >
    > Then invoke it using an NSTask in the main app:
    >
    > NSTask *task = [[NSTask alloc] init];
    > [task setLaunchPath:pathToHelper];
    > [task setArguments:[NSArray arrayWithObject:[[NSBundle mainBundle]
    > bundlePath]]];
    > [task setStandardInput:[NSPipe pipe]];
    > [task launch];
    >
    > And then quit the app. The subtask will block in the read waiting for
    > data on the pipe which will never come. When your app terminates, the
    > write end of the pipe closes, unblocking the read in the subtask. The
    > subtask then relaunches your app using the URL you gave it as a
    > parameter. It's fast, easy to use, and requires no polling or messy
    > shell scripts.
    >
    > Mike
    >
  • Maybe I'm missing something, but....

    Instead of NSTask, why not use LSOpenApplication passing in
    (kLSLaunchDefaults | kLSLaunchNewInstance | kLSLaunchDontAddToRecents)
    for flags? Your main program will block until the sub program has
    launched.

    Joe Ranieri
    Lead Magician, Alacatia Labs, Inc.
    http://alacatia.com
  • On 3 mar 2008, at 20.43, Steven Degutis wrote:

    > Perhaps a small application like relaunch.app could be imbedded into
    > your relaunchable application, where all it pretty much does is serve
    > as a buffer by relaunching your application reliably (through Cocoa
    > methods) after your app calls it the same way you mentioned above.
    >
    > [[NSWorkspace sharedWorkspace]
    > launchApplication:relauncherExecutablePath];
    > [NSApp terminate:self];

    Sure, that's a reasonable scenario.

    > Then the relaunch.app program uses a method which calls itself
    > continuously (like a loop that wont make your app unresponsive), which
    > does a check to see if the original app is still running using
    > NSWorkspace's -launchedApplications, and only after it sees your app
    > end, then it will run the app again and terminate itself. This ensures
    > that your app opens only after it ends first. And doing this would be
    > quite easy, using two methods, -awakeFromNib and your custom method
    > -hasAppEnded. The former would launch the second one immediately, and
    > do nothing else, and the second one would do the check, and at the
    > end, if it is still running (hasnt quit and run the new app) it would
    > launch itself with [self performSelector:@selector(hasAppEnded)
    > withObject:nil afterDelay:1.0]; or something. This is what I would do
    > anyway. 1.0 might be too long or too short for a real application,
    > though.

    I think I find Sparkle's way of doing this more elegant (you don't
    have a loop, but listen for notifications - if the original process
    has not already terminated).
    http://code.andymatuschak.org/sparkle/trunk/relaunch.m

    > On Mon, Mar 3, 2008 at 10:24 AM, Mattias Arrelid
    > <mattias...> wrote:
    >> Hi everyone,
    >>
    >> I have an application that I need to restart. An easy way to do this
    >> could be:
    >>
    >> [[NSWorkspace sharedWorkspace] launchApplication:executablePath];
    >> [NSApp terminate:self];
    >>
    >> The problem is that the above solution results in confusion in the
    >> system dock; sometimes the original application does manage to quit
    >> before the new instance, other times not. If it does NOT manage to
    >> quit before the new one is launched, the dock ends up with two icons
    >> for my application; one that indicates that the application is
    >> launched, another one that's indicates that it's not launched. This
    >> is
    >> NOT good.
    >>
    >> I've seen other programs in need of a restart (e.g. the Sparkle
    >> framework) using a separate helper application that simply checks
    >> whether the original process has quit properly before re-launching
    >> it.
    >> This sure does work, but it isn't a pretty solution.
    >>
    >> Another solution could be something like:
    >>
    >> system("nohup sh -c 'sleep 1; open -b com.company.identifier'");
    >> exit(0);
    >>
    >> This is (in my humble opinion) better, but still, not very elegant.
    >>
    >> Wouldn't it be nice if we could have something like [NSApp relaunch],
    >> [NSApp relaunchWithArguments:argArray] etc. to deal with stuff like
    >> this? Or does such calls imply things that I'm overseeing right now?
    >> Maybe there are similar methods already available in the system, that
    >> accomplishes stuff like this?
    >>
    >> Any ideas/suggestions on the above are more than welcome.
    >>
    >> Thanks in advance.
    >> Mattias

    But again, maybe I should file a request since this actually is
    something that several application developers could want to do.

    / Mattias
  • On Mon, Mar 3, 2008 at 9:27 PM, Joe Ranieri <joe...> wrote:
    > Maybe I'm missing something, but....
    >
    > Instead of NSTask, why not use LSOpenApplication passing in
    > (kLSLaunchDefaults | kLSLaunchNewInstance | kLSLaunchDontAddToRecents)
    > for flags? Your main program will block until the sub program has
    > launched.

    There are two problems with this. First, the new instance of the app
    will not have functional Services, if it happens to export any. The
    registration will be blocked because the first app is still around.
    Second, the new instance will get a new Dock icon. This is very ugly
    if the app has a permanent place in the dock, since you'll end up with
    two icons, one non-running icon in the middle somewhere, and one
    running icon at the end.

    On another note, a couple of people have proposed ideas where a
    subtask sits around watching for application terminated notifications,
    then relaunches based on that. This is a fine approach, although IMO
    more complex than my technique of sitting on a pipe. However, after
    you sign up for the notification you must be sure to check to see if
    the parent app is still around, because it could have quit already.
    This is unlikely but it's possible, so a proper implementation of this
    technique needs to handle that case. This is another reason I prefer
    the pipe technique, as it will always work no matter when the parent
    app terminates. The Sparkle code which Mattias Arrelid linked to
    appears to handle this properly, although mixing NSWorkspace
    notifications with POSIX process management makes me slightly
    paranoid.

    Mike
previous month march 2008 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today