代码之家  ›  专栏  ›  技术社区  ›  slycrel

自动更新后重新启动我的应用程序?

  •  3
  • slycrel  · 技术社区  · 15 年前

    在OSX中,如何在下载更新版本后自动重新启动应用程序?

    我已经环顾了一些地方,launchd似乎是实现这一目标的可能方法,但我似乎无法理解它。我似乎也找不到任何好的资源专门讨论这个问题。

    我也可以创建一个脚本或单独的过程来完成这项工作,但这看起来很笨拙,我希望有更好的解决方案。

    3 回复  |  直到 15 年前
        1
  •  3
  •   Julia Mironova    15 年前

    单独的进程是一个很好的解决方案,看看Sparkle框架(大多数应用程序使用它进行自动更新),它们也用于这个独立的应用程序- https://github.com/andymatuschak/Sparkle/blob/master/relaunch.m

        2
  •  2
  •   Colin    13 年前

    只是扩展一下Julia的答案,因为我不得不重新启动我的应用程序,而不是更新,所以我研究了Sparkle是如何做到的-

    Sparkle的最新版本(截至2011年11月)有一个名为finish_installation.app的项目目标,它包含在Sparkle框架的Resources目录中。Sparkle作为主机应用程序的一部分运行,它将finish_应用程序复制到application_支持目录,并使用launched运行其二进制可执行文件,如下图所示,传递主机进程id和重新启动路径:

    NSString *relaunchToolPath = [NSString stringWithFormat:@"%@/finish_installation.app/Contents/MacOS/finish_installation", tempDir];
    [NSTask launchedTaskWithLaunchPath: relaunchToolPath arguments:[NSArray arrayWithObjects:pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], tempDir, relaunch ? @"1" : @"0", nil]];
    [NSApp terminate:self];
    

    似乎有了这个函数,当父进程退出时(?),将启动finish_应用程序的父级。

    finish_installation等待传入的进程id消失,并进行初始检查以查看是否启动了其父进程(pid=1)

    if(getppid() == 1)...
    if (GetProcessForPID(parentprocessid, &psn) == procNotFound)...
    

    然后启动应用程序:

    [[NSWorkspace sharedWorkspace] openFile: appPath];
    

    最后一个有趣的小贴士:如果安装需要很长时间,那么finish_installation会将自身更改为前台进程,以便用户可以看到某些应用正在运行:

    ProcessSerialNumber     psn = { 0, kCurrentProcess };
    TransformProcessType( &psn, kProcessTransformToForegroundApplication );
    
        3
  •  0
  •   Sawan Cool    8 年前

    Source Blog

    - (void) restartOurselves
        {
           //$N = argv[N]
           NSString *killArg1AndOpenArg2Script = @"kill -9 $1 \n open \"$2\"";
    
           //NSTask needs its arguments to be strings
           NSString *ourPID = [NSString stringWithFormat:@"%d",
                          [[NSProcessInfo processInfo] processIdentifier]];
    
           //this will be the path to the .app bundle,
           //not the executable inside it; exactly what `open` wants
           NSString * pathToUs = [[NSBundle mainBundle] bundlePath];
    
           NSArray *shArgs = [NSArray arrayWithObjects:@"-c", // -c tells sh to execute the next argument, passing it the remaining arguments.
                        killArg1AndOpenArg2Script,
                        @"", //$0 path to script (ignored)
                        ourPID, //$1 in restartScript
                        pathToUs, //$2 in the restartScript
                        nil];
           NSTask *restartTask = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:shArgs];
           [restartTask waitUntilExit]; //wait for killArg1AndOpenArg2Script to finish
           NSLog(@"*** ERROR: %@ should have been terminated, but we are still running", pathToUs);
           assert(!"We should not be running!");
        }