深入浅出Electron:原理、工程与实践
上QQ阅读APP看书,第一时间看更新

4.3 Windows应用升级原理

确认下载的文件正确无误后,electron-updater将会触发update-downloaded事件,此时用户代码会请求用户确认升级,用户确认后将会执行electron-updater的quitAndInstall方法,此方法会退出当前应用,安装新下载的安装文件。

electron-updater除了在准备好升级安装包文件后触发update-downloaded事件外,在下载和检验过程中也会触发download-progress和checking-for-update事件,以便用户显示过程信息。如果下载的文件验证无效,electron-updater也会触发update-not-available事件。

退出当前应用并不难,electron本身就提供了API,代码如下所示:

require("electron").app.quit()

此方法执行后,一般情况下应用的大部分在途逻辑当即停止执行并释放资源,接着触发electron的quit事件,代码如下:

require("electron").app.once("quit", (_: Event, exitCode: number) => handler (exitCode))

启动新版本的安装文件的逻辑即在上述handler方法内执行,代码逻辑如下:

doInstall(options: InstallOptions): boolean {
  const args = ["--updated"]
  if (options.isSilent) {
    args.push("/S")
  }
  if (options.isForceRunAfter) {
    args.push("--force-run")
  }
  const packagePath = this.downloadedUpdateHelper == null ? null : this.down loadedUpdateHelper.packageFile
  if (packagePath != null) {
    args.push('--package-file=${packagePath}')
  }
  const callUsingElevation = (): void => {
    _spawn(path.join(process.resourcesPath!!, "elevate.exe"), [options.installer Path].concat(args))
      .catch(e => this.dispatchError(e))
  }
  if (options.isAdminRightsRequired) {
    this._logger.info("isAdminRightsRequired is set to true, run installer using elevate.exe")
    callUsingElevation()
    return true
  }
  _spawn(options.installerPath, args)
    .catch((e: Error) => {
      const errorCode = (e as NodeJS.ErrnoException).code
      this._logger.info('Cannot run installer: error code: ${errorCode}, error message: "${e.message}", will be executed again using elevate if EACCES"')
      if (errorCode === "UNKNOWN" || errorCode === "EACCES") {
        callUsingElevation()
      }
      else {
        this.dispatchError(e)
      }
    })
  return true
}

electron-updater使用了Node.js内置的child_process库启动安装文件,因为安装文件是通过NSIS打包工具打包而成,所以可以接纳一系列的命令行参数:

  • --updated:以升级模式启动安装包,如果当前应用正在执行,则提示用户退出当前应用再安装升级包。
  • /S:以静默安装方式启动安装包,静默安装方式不会显示任何界面,即使当前应用正在运行也不会提示用户退出程序。

如果打包应用时为electron-builder提供了如下配置,则应用安装新版本安装包时,会以管理员的身份启动新版本安装包。

electronBuilder.build({
  config: {
    win:{
      requestedExecutionLevel: "requireAdministrator", // 或highestAvailable
      ...
    },
    ...
  },
  ...
})

如果启动应用失败,得到了EACCES或UNKNOWN的错误码,也会尝试以管理员的身份启动安装包。

这个操作是通过elevate.exe完成的,这个可执行程序是electron-builder通过NSIS内置到应用程序内的,文件存放路径如下:

C:\Program Files (x86)\yourProductName\resources\elevate.exe

在命令行下执行此程序,并把目标程序路径当做参数传入命令行,目标程序则以管理员身份启动运行。它还支持很多其他的参数,比如等待目标程序退出、为目标程序传参等。你可以通过如下命令行指令查看其支持的参数:

> cd C:\Program Files (x86)\yourProductName\resources\
> elevate -?

这是一个非常有用的工具,虽然electron-builder携带它是为了给自己服务的,但我们也可以正常使用它,假设你的产品需要以管理员的身份启动一个第三方应用程序,就可以通过Node.js的内置模块child_process来使用elevate.exe启动你的目标程序。