【问题标题】:restart my app after an auto-update?自动更新后重新启动我的应用程序?
【发布时间】:2011-05-15 07:40:38
【问题描述】:

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

我环顾四周,launchd 似乎是这样做的可能方式,但我似乎无法理解它。我似乎也找不到任何好的资源来专门讨论这个问题。

我也可以创建一个脚本或单独的进程来执行此操作,但这似乎很笨拙,我期待那里有更好的解决方案。

【问题讨论】:

标签: cocoa macos launchd application-restart


【解决方案1】:

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

【讨论】:

  • 感谢有关 sparkle Julia 的提醒,它看起来很有趣。如果我必须采用单独的流程路线,我会将您标记为我的答案。希望在不必编写单独的应用程序的情况下有所收获。
  • 不要重新发明轮子。使用闪光。这是一个久经考验的解决方案,被 OS X 上的数千个第 3 方应用程序使用。
【解决方案2】:

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

Sparkle 的最新版本(截至 11/2011)有一个名为 finish_installation.app 的项目目标,它包含在 Sparkle 框架的 Resources 目录中。 Sparkle 作为宿主 App 的一部分运行,将 finish_application 复制到 application_support 目录并使用 launch 来运行其二进制可执行文件,如下所示,传入宿主进程 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_application的父进程启动。

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】:

    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!");
        }
    

    【讨论】:

    • 我不知道为什么,但是当我尝试在 GUI 应用程序中做类似的事情时,它总是为子进程生成 signal: killed
    • 原来我覆盖了我的应用程序的可执行文件后,MacOS 不再允许它启动任何东西。所以我必须首先启动“restarter”进程并让它等待信号来进行实际的重启。
    猜你喜欢
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多